Repository: GameTechDev/OutdoorLightScattering Branch: master Commit: 3b31b3b8c1aa Files: 168 Total size: 15.6 MB Directory structure: gitextract_u9hxjrts/ ├── .gitattributes ├── .gitignore ├── CPUT/ │ ├── CPUT/ │ │ ├── CPUT.h │ │ ├── CPUTAssetLibrary.cpp │ │ ├── CPUTAssetLibrary.h │ │ ├── CPUTAssetLibraryDX11.cpp │ │ ├── CPUTAssetLibraryDX11.h │ │ ├── CPUTAssetSet.cpp │ │ ├── CPUTAssetSet.h │ │ ├── CPUTAssetSetDX11.cpp │ │ ├── CPUTAssetSetDX11.h │ │ ├── CPUTBuffer.cpp │ │ ├── CPUTBuffer.h │ │ ├── CPUTBufferDX11.cpp │ │ ├── CPUTBufferDX11.h │ │ ├── CPUTButton.cpp │ │ ├── CPUTButton.h │ │ ├── CPUTCallbackHandler.h │ │ ├── CPUTCamera.cpp │ │ ├── CPUTCamera.h │ │ ├── CPUTCheckbox.cpp │ │ ├── CPUTCheckbox.h │ │ ├── CPUTComputeShaderDX11.cpp │ │ ├── CPUTComputeShaderDX11.h │ │ ├── CPUTConfigBlock.cpp │ │ ├── CPUTConfigBlock.h │ │ ├── CPUTControl.cpp │ │ ├── CPUTControl.h │ │ ├── CPUTDomainShaderDX11.cpp │ │ ├── CPUTDomainShaderDX11.h │ │ ├── CPUTDropdown.cpp │ │ ├── CPUTDropdown.h │ │ ├── CPUTEventHandler.h │ │ ├── CPUTFont.cpp │ │ ├── CPUTFont.h │ │ ├── CPUTFontDX11.cpp │ │ ├── CPUTFontDX11.h │ │ ├── CPUTFrustum.cpp │ │ ├── CPUTFrustum.h │ │ ├── CPUTGeometryShaderDX11.cpp │ │ ├── CPUTGeometryShaderDX11.h │ │ ├── CPUTGuiController.cpp │ │ ├── CPUTGuiController.h │ │ ├── CPUTGuiControllerDX11.cpp │ │ ├── CPUTGuiControllerDX11.h │ │ ├── CPUTHullShaderDX11.cpp │ │ ├── CPUTHullShaderDX11.h │ │ ├── CPUTITTTaskMarker.cpp │ │ ├── CPUTITTTaskMarker.h │ │ ├── CPUTInputLayoutCache.h │ │ ├── CPUTInputLayoutCacheDX11.cpp │ │ ├── CPUTInputLayoutCacheDX11.h │ │ ├── CPUTLight.cpp │ │ ├── CPUTLight.h │ │ ├── CPUTMaterial.cpp │ │ ├── CPUTMaterial.h │ │ ├── CPUTMaterialDX11.cpp │ │ ├── CPUTMaterialDX11.h │ │ ├── CPUTMath.h │ │ ├── CPUTMesh.cpp │ │ ├── CPUTMesh.h │ │ ├── CPUTMeshDX11.cpp │ │ ├── CPUTMeshDX11.h │ │ ├── CPUTModel.cpp │ │ ├── CPUTModel.h │ │ ├── CPUTModelDX11.cpp │ │ ├── CPUTModelDX11.h │ │ ├── CPUTNullNode.cpp │ │ ├── CPUTNullNode.h │ │ ├── CPUTOSServicesWin.cpp │ │ ├── CPUTOSServicesWin.h │ │ ├── CPUTPerfTaskMarker.cpp │ │ ├── CPUTPerfTaskMarker.h │ │ ├── CPUTPixelShaderDX11.cpp │ │ ├── CPUTPixelShaderDX11.h │ │ ├── CPUTPostProcess.cpp │ │ ├── CPUTPostProcess.h │ │ ├── CPUTRefCount.h │ │ ├── CPUTRenderNode.cpp │ │ ├── CPUTRenderNode.h │ │ ├── CPUTRenderParams.h │ │ ├── CPUTRenderParamsDX.h │ │ ├── CPUTRenderStateBlock.cpp │ │ ├── CPUTRenderStateBlock.h │ │ ├── CPUTRenderStateBlockDX11.cpp │ │ ├── CPUTRenderStateBlockDX11.h │ │ ├── CPUTRenderStateMapsDX11.h │ │ ├── CPUTRenderTarget.cpp │ │ ├── CPUTRenderTarget.h │ │ ├── CPUTResource.h │ │ ├── CPUTShaderDX11.cpp │ │ ├── CPUTShaderDX11.h │ │ ├── CPUTSlider.cpp │ │ ├── CPUTSlider.h │ │ ├── CPUTSprite.cpp │ │ ├── CPUTSprite.h │ │ ├── CPUTText.cpp │ │ ├── CPUTText.h │ │ ├── CPUTTexture.cpp │ │ ├── CPUTTexture.h │ │ ├── CPUTTextureDX11.cpp │ │ ├── CPUTTextureDX11.h │ │ ├── CPUTTimer.h │ │ ├── CPUTTimerWin.cpp │ │ ├── CPUTTimerWin.h │ │ ├── CPUTVertexShaderDX11.cpp │ │ ├── CPUTVertexShaderDX11.h │ │ ├── CPUTWindowWin.cpp │ │ ├── CPUTWindowWin.h │ │ ├── CPUT_DX11.cpp │ │ └── CPUT_DX11.h │ ├── CPUT-DX11.sln │ ├── CPUT-DX11.vcxproj │ ├── CPUT-DX11.vcxproj.filters │ ├── CPUT-DX11_2012.vcxproj │ ├── CPUT-DX11_2012.vcxproj.filters │ └── resources/ │ ├── controls/ │ │ ├── atlas/ │ │ │ ├── atlas.cmd │ │ │ ├── atlas.set │ │ │ ├── lambert1.mtl │ │ │ └── polySurface1.mdl │ │ └── atlas.dds │ ├── fonts/ │ │ └── font_Arial_12.dds │ └── shaders/ │ ├── GUIRenderState.rs │ ├── GUIShaderDX.ps │ └── GUIShaderDX.vs ├── Default_Config.txt ├── LightSctrPostProcess.cpp ├── LightSctrPostProcess.h ├── OutdoorLightScattering.cpp ├── OutdoorLightScattering.h ├── OutdoorLightScattering_2010.rc ├── OutdoorLightScattering_2010.sln ├── OutdoorLightScattering_2010.vcxproj ├── OutdoorLightScattering_2010.vcxproj.filters ├── OutdoorLightScattering_2012.sln ├── OutdoorLightScattering_2012.vcxproj ├── OutdoorLightScattering_2012.vcxproj.filters ├── RenderTechnique.cpp ├── RenderTechnique.h ├── ShaderMacroHelper.h ├── Terrain/ │ ├── ConfigFile.cpp │ ├── DynamicQuadTreeNode.h │ ├── EarthHemisphere.cpp │ ├── EarthHemisphere.h │ ├── ElevationDataSource.cpp │ ├── ElevationDataSource.h │ ├── Errors.h │ ├── HierarchyArray.h │ └── stdafx.cpp ├── fx/ │ ├── Common.fxh │ ├── LightScattering.fx │ ├── RefineSampleLocations.fx │ ├── Structures.fxh │ ├── Terrain.fx │ └── TerrainStructs.fxh ├── license.txt ├── media/ │ ├── Terrain/ │ │ └── HeightMap.tif │ └── Tiles/ │ ├── cliff_DM.dds │ ├── cliff_NM.dds │ ├── grassDark_DM.dds │ ├── grass_DM.dds │ ├── grass_NM.dds │ ├── gravel_DM.dds │ ├── gravel_NM.dds │ └── snow_DM.dds ├── readme.txt ├── resource.h └── stdafx.h ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitattributes ================================================ # Auto detect text files and perform LF normalization * text=auto # Custom for Visual Studio *.cs diff=csharp *.sln merge=union *.csproj merge=union *.vbproj merge=union *.fsproj merge=union *.dbproj merge=union # Standard to msysgit *.doc diff=astextplain *.DOC diff=astextplain *.docx diff=astextplain *.DOCX diff=astextplain *.dot diff=astextplain *.DOT diff=astextplain *.pdf diff=astextplain *.PDF diff=astextplain *.rtf diff=astextplain *.RTF diff=astextplain ================================================ FILE: .gitignore ================================================ ################# ## Eclipse ################# *.pydevproject .project .metadata bin/ tmp/ *.tmp *.bak *.swp *~.nib local.properties .classpath .settings/ .loadpath # External tool builders .externalToolBuilders/ # Locally stored "Eclipse launch configurations" *.launch # CDT-specific .cproject # PDT-specific .buildpath ################# ## Visual Studio ################# ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. # User-specific files *.suo *.user *.sln.docstates # Build results [Dd]ebug/ [Rr]elease/ x64/ build/ [Bb]in/ [Oo]bj/ # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* *_i.c *_p.c *.ilk *.meta *.obj *.pch *.pdb *.pgc *.pgd *.rsp *.sbr *.tlb *.tli *.tlh *.tmp *.tmp_proj *.log *.vspscc *.vssscc .builds *.pidb *.log *.scc # Visual C++ cache files ipch/ *.aps *.ncb *.opensdf *.sdf *.cachefile # Visual Studio profiler *.psess *.vsp *.vspx # Guidance Automation Toolkit *.gpState # ReSharper is a .NET coding add-in _ReSharper*/ *.[Rr]e[Ss]harper # TeamCity is a build add-in _TeamCity* # DotCover is a Code Coverage Tool *.dotCover # NCrunch *.ncrunch* .*crunch*.local.xml # Installshield output folder [Ee]xpress/ # DocProject is a documentation generator add-in DocProject/buildhelp/ DocProject/Help/*.HxT DocProject/Help/*.HxC DocProject/Help/*.hhc DocProject/Help/*.hhk DocProject/Help/*.hhp DocProject/Help/Html2 DocProject/Help/html # Click-Once directory publish/ # Publish Web Output *.Publish.xml *.pubxml # NuGet Packages Directory ## TODO: If you have NuGet Package Restore enabled, uncomment the next line #packages/ # Windows Azure Build Output csx *.build.csdef # Windows Store app package directory AppPackages/ # Others sql/ *.Cache ClientBin/ [Ss]tyle[Cc]op.* ~$* *~ *.dbmdl *.[Pp]ublish.xml *.pfx *.publishsettings # RIA/Silverlight projects Generated_Code/ # Backup & report files from converting an old project file to a newer # Visual Studio version. Backup files are not needed, because we have git ;-) _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML UpgradeLog*.htm # SQL Server files App_Data/*.mdf App_Data/*.ldf ############# ## Windows detritus ############# # Windows image file caches Thumbs.db ehthumbs.db # Folder config file Desktop.ini # Recycle Bin used on file shares $RECYCLE.BIN/ # Mac crap .DS_Store ############# ## Python ############# *.py[co] # Packages *.egg *.egg-info dist/ build/ eggs/ parts/ var/ sdist/ develop-eggs/ .installed.cfg # Installer logs pip-log.txt # Unit test / coverage reports .coverage .tox #Translations *.mo #Mr Developer .mr.developer.cfg ================================================ FILE: CPUT/CPUT/CPUT.h ================================================ //////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); you may not // use this file except in compliance with the License. You may obtain a copy // of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the // License for the specific language governing permissions and limitations // under the License. //////////////////////////////////////////////////////////////////////////////// #ifndef __CPUTBASE_H__ #define __CPUTBASE_H__ // Master #defines for which target #define CPUT_FOR_DX11 #include #include #include #include #include #include #include "CPUTMath.h" #include "CPUTEventHandler.h" #include "CPUTCallbackHandler.h" #include "CPUTTimer.h" #ifdef CPUT_GPA_INSTRUMENTATION // For D3DPERF_* calls, you also need d3d9.lib included #include // required for all the pix D3DPERF_BeginEvent()/etc calls #include #include "CPUTITTTaskMarker.h" // markup helper for GPA Platform Analyzer tags #include "CPUTPerfTaskMarker.h" // markup helper for GPA Frame Analyzer tags // GLOBAL instrumentation junk enum CPUT_GPA_INSTRUMENTATION_STRINGS{ GPA_HANDLE_CPUT_CREATE = 0, GPA_HANDLE_CONTEXT_CREATION, GPA_HANDLE_SYSTEM_INITIALIZATION, GPA_HANDLE_MAIN_MESSAGE_LOOP, GPA_HANDLE_EVENT_DISPATCH_AND_HANDLE, GPA_HANDLE_LOAD_SET, GPA_HANDLE_LOAD_MODEL, GPA_HANDLE_LOAD_MATERIAL, GPA_HANDLE_LOAD_TEXTURE, GPA_HANDLE_LOAD_CAMERAS, GPA_HANDLE_LOAD_LIGHTS, GPA_HANDLE_LOAD_VERTEX_SHADER, GPA_HANDLE_LOAD_GEOMETRY_SHADER, GPA_HANDLE_LOAD_PIXEL_SHADER, GPA_HANDLE_DRAW_GUI, GPA_HANDLE_STRING_ENUMS_SIZE, }; #endif // CPUT_GPA_INSTRUMENTATION // Heap corruption, ASSERT, and TRACE defines //----------------------------------------------------------------------------- #ifdef _DEBUG #include #define TRACE(String) {OutputDebugString(String);} #define DEBUGMESSAGEBOX(Title, Text) { CPUTOSServices::GetOSServices()->OpenMessageBox(Title, Text);} #define ASSERT(Condition, Message) { if( !(Condition) ) { cString msg = cString(_T(__FUNCTION__)) + _L(": ") + Message; OutputDebugString(msg.c_str()); DEBUGMESSAGEBOX(_L("Assert"), msg ); } assert(Condition);} #define HEAPCHECK { int heapstatus = _heapchk(); ASSERT(_HEAPOK == heapstatus, _L("Heap corruption") ); } // #define HEAPCHECK {} #else #define ASSERT(Condition, Message) #define TRACE(String) #define DEBUGMESSAGEBOX(Title, Text) #define HEAPCHECK #endif // _DEBUG // Error codes //----------------------------------------------------------------------------- typedef enum CPUTResult { // success CPUT_SUCCESS = 0x00000000, // warnings // CPUT_WARNING_OUT_OF_RANGE, CPUT_WARNING_NOT_FOUND, // CPUT_WARNING_ALREADY_EXISTS, // CPUT_WARNING_FILE_IN_SEARCH_PATH_BUT_NOT_WHERE_SPECIFIED, // CPUT_WARNING_PHONG_SHADER_MISSING_TEXTURE, CPUT_WARNING_CANCELED, // CPUT_WARNING_NO_SUITABLE_FORMAT_FOUND, // CPUT_WARNING_SHADER_INPUT_SLOT_NOT_MATCHED, // // // file errors CPUT_ERROR_FILE_NOT_FOUND = 0xF0000001, CPUT_ERROR_FILE_READ_ERROR = CPUT_ERROR_FILE_NOT_FOUND+1, CPUT_ERROR_FILE_CLOSE_ERROR = CPUT_ERROR_FILE_NOT_FOUND+2, CPUT_ERROR_FILE_IO_ERROR = CPUT_ERROR_FILE_NOT_FOUND+3, CPUT_ERROR_FILE_NO_SUCH_DEVICE_OR_ADDRESS = CPUT_ERROR_FILE_NOT_FOUND+4, CPUT_ERROR_FILE_BAD_FILE_NUMBER = CPUT_ERROR_FILE_NOT_FOUND+5, CPUT_ERROR_FILE_NOT_ENOUGH_MEMORY = CPUT_ERROR_FILE_NOT_FOUND+6, CPUT_ERROR_FILE_PERMISSION_DENIED = CPUT_ERROR_FILE_NOT_FOUND+7, CPUT_ERROR_FILE_DEVICE_OR_RESOURCE_BUSY = CPUT_ERROR_FILE_NOT_FOUND+8, CPUT_ERROR_FILE_EXISTS = CPUT_ERROR_FILE_NOT_FOUND+9, CPUT_ERROR_FILE_IS_A_DIRECTORY = CPUT_ERROR_FILE_NOT_FOUND+10, CPUT_ERROR_FILE_TOO_MANY_OPEN_FILES = CPUT_ERROR_FILE_NOT_FOUND+11, CPUT_ERROR_FILE_TOO_LARGE = CPUT_ERROR_FILE_NOT_FOUND+12, CPUT_ERROR_FILE_DEVICE_FULL = CPUT_ERROR_FILE_NOT_FOUND+13, CPUT_ERROR_FILE_FILENAME_TOO_LONG = CPUT_ERROR_FILE_NOT_FOUND+14, CPUT_ERROR_FILE_PATH_ERROR = CPUT_ERROR_FILE_NOT_FOUND+15, CPUT_ERROR_FILE_ERROR = CPUT_ERROR_FILE_NOT_FOUND+16, // // CPUT_ERROR_DIRECTORY_NOT_FOUND = CPUT_ERROR_FILE_NOT_FOUND+21, // // // subsystem errors CPUT_ERROR_INVALID_PARAMETER = 0xF0000100, CPUT_ERROR_NOT_FOUND = CPUT_ERROR_INVALID_PARAMETER+1, // CPUT_ERROR_COMPONENT_NOT_INITIALIZED = CPUT_ERROR_INVALID_PARAMETER+2, // CPUT_ERROR_SUBSYSTEM_OUT_OF_MEMORY = CPUT_ERROR_INVALID_PARAMETER+3, // CPUT_ERROR_OUT_OF_BOUNDS = CPUT_ERROR_INVALID_PARAMETER+4, // CPUT_ERROR_HEAP_CORRUPTION = CPUT_ERROR_INVALID_PARAMETER+5, // // // image format errors CPUT_ERROR_UNSUPPORTED_IMAGE_FORMAT = 0xF0000200, // CPUT_ERROR_ERROR_LOADING_IMAGE = CPUT_ERROR_UNSUPPORTED_IMAGE_FORMAT+1, CPUT_ERROR_UNSUPPORTED_SRGB_IMAGE_FORMAT, // // // shader loading errors // CPUT_SHADER_LOAD_ERROR = 0xF0000300, // CPUT_SHADER_COMPILE_ERROR = CPUT_SHADER_LOAD_ERROR+1, // CPUT_SHADER_LINK_ERROR = CPUT_SHADER_LOAD_ERROR+2, // CPUT_SHADER_REGISTRATION_ERROR = CPUT_SHADER_LOAD_ERROR+3, // CPUT_SHADER_CONSTANT_BUFFER_ERROR = CPUT_SHADER_LOAD_ERROR+4, // CPUT_SHADER_REFLECTION_ERROR = CPUT_SHADER_LOAD_ERROR+5, // // // texture loading errors CPUT_TEXTURE_LOAD_ERROR = 0xF0000400, CPUT_ERROR_TEXTURE_FILE_NOT_FOUND = CPUT_TEXTURE_LOAD_ERROR+1, // // // GUI errors CPUT_GUI_GEOMETRY_CREATION_ERROR = 0xF0000500, // CPUT_GUI_SAMPLER_CREATION_ERROR = CPUT_GUI_GEOMETRY_CREATION_ERROR+1, // CPUT_GUI_TEXTURE_CREATION_ERROR = CPUT_GUI_GEOMETRY_CREATION_ERROR+2, // CPUT_GUI_CANNOT_CREATE_CONTROL = CPUT_GUI_GEOMETRY_CREATION_ERROR+3, CPUT_GUI_INVALID_CONTROL_ID = CPUT_GUI_GEOMETRY_CREATION_ERROR+4, // // // Texture loading errors // CPUT_FONT_TEXTURE_TYPE_ERROR = 0xF0000600, // CPUT_FONT_TEXTURE_LOAD_ERROR = CPUT_FONT_TEXTURE_TYPE_ERROR+1, // // // Model loading errors // CPUT_ERROR_MODEL_LOAD_ERROR = 0xF0000650, // CPUT_ERROR_MODEL_FILE_NOT_FOUND = CPUT_ERROR_MODEL_LOAD_ERROR+1, // // // Shader errors CPUT_ERROR_VERTEX_LAYOUT_PROBLEM = 0xF0000700, // CPUT_ERROR_VERTEX_BUFFER_CREATION_PROBLEM = CPUT_ERROR_VERTEX_LAYOUT_PROBLEM+1, // CPUT_ERROR_INDEX_BUFFER_CREATION_PROBLEM = CPUT_ERROR_VERTEX_LAYOUT_PROBLEM+2, // CPUT_ERROR_UNSUPPORTED_VERTEX_ELEMENT_TYPE = CPUT_ERROR_VERTEX_LAYOUT_PROBLEM+3, // CPUT_ERROR_INDEX_BUFFER_LAYOUT_PROBLEM = CPUT_ERROR_VERTEX_LAYOUT_PROBLEM+4, CPUT_ERROR_SHADER_INPUT_SLOT_NOT_MATCHED = CPUT_ERROR_VERTEX_LAYOUT_PROBLEM+5, // // // // Context creation errors // CPUT_ERROR_CONTEXT_CREATION_FAILURE = 0xF0000C00, // CPUT_ERROR_SWAP_CHAIN_CREATION_FAILURE = CPUT_ERROR_CONTEXT_CREATION_FAILURE+1, // CPUT_ERROR_RENDER_TARGET_VIEW_CREATION_FAILURE = CPUT_ERROR_CONTEXT_CREATION_FAILURE+2, // // // Depth buffer errors // CPUT_ERROR_DEPTH_BUFFER_CREATION_ERROR = 0xF0000800, // CPUT_ERROR_DEPTH_STENCIL_BUFFER_CREATION_ERROR = CPUT_ERROR_DEPTH_BUFFER_CREATION_ERROR+1, // CPUT_ERROR_RASTER_STATE_CREATION_ERROR = CPUT_ERROR_DEPTH_BUFFER_CREATION_ERROR+2, // // // GUI shaders CPUT_ERROR_INITIALIZATION_GUI_VERTEX_SHADER_NOT_FOUND = 0xF0000130, CPUT_ERROR_INITIALIZATION_GUI_PIXEL_SHADER_NOT_FOUND = CPUT_ERROR_INITIALIZATION_GUI_VERTEX_SHADER_NOT_FOUND+1, CPUT_ERROR_INITIALIZATION_GUI_CONTROL_TEXTURES_NOT_FOUND = CPUT_ERROR_INITIALIZATION_GUI_VERTEX_SHADER_NOT_FOUND+2, // // // gfx system errors // CPUT_ERROR_GFX_SUBSYSTEM_BUSY = 0xF0000B00, // CPUT_ERROR_GFX_SUBSYSTEM_TO_MANY_OBJECTS = CPUT_ERROR_GFX_SUBSYSTEM_BUSY+1, // // // window layer errors CPUT_ERROR_WINDOW_CANNOT_REGISTER_APP = 0xF0000D00, CPUT_ERROR_WINDOW_ALREADY_EXISTS = CPUT_ERROR_WINDOW_CANNOT_REGISTER_APP+1, // CPUT_ERROR_CANNOT_GET_WINDOW_CLASS = CPUT_ERROR_WINDOW_CANNOT_REGISTER_APP+3, CPUT_ERROR_CANNOT_GET_WINDOW_INSTANCE = CPUT_ERROR_WINDOW_CANNOT_REGISTER_APP+4, // CPUT_ERROR_WINDOW_OS_PROPERTY_GET_ERROR = CPUT_ERROR_WINDOW_CANNOT_REGISTER_APP+5, // // // AssetLibrary/AssetSet errors CPUT_ERROR_ASSET_LIBRARY_INVALID_LIBRARY = 0xF0000E00, // CPUT_ERROR_ASSET_SET_INVALID_TYPE = CPUT_ERROR_ASSET_LIBRARY_INVALID_LIBRARY+1, // CPUT_ERROR_ASSET_LIBRARY_OBJECT_NOT_FOUND, // CPUT_ERROR_ASSET_ALREADY_EXISTS = CPUT_ERROR_ASSET_LIBRARY_INVALID_LIBRARY+3, // // // Paramter block errors. CPUT_ERROR_PARAMETER_BLOCK_NOT_FOUND = 0xF0000F00, // // // misc errors // CPUT_ERROR_FULLSCREEN_SWITCH_ERROR = 0xF0000F00, } CPUTResult; static int gRefCount = 0; //handy defines //----------------------------------------------------------------------------- #define SAFE_RELEASE(p) {if((p)){HEAPCHECK; gRefCount = (p)->Release(); (p)=NULL; HEAPCHECK;} } #define SAFE_DELETE(p) {if((p)){HEAPCHECK; delete (p); (p)=NULL;HEAPCHECK; }} #define SAFE_DELETE_ARRAY(p){if((p)){HEAPCHECK; delete[](p); (p)=NULL;HEAPCHECK; }} #define UNREFERENCED_PARAMETER(P) (P) // CPUT data types //----------------------------------------------------------------------------- #define CPUTSUCCESS(returnCode) ((returnCode) < 0xF0000000) #define CPUTFAILED(returnCode) ((returnCode) >= 0xF0000000) //typedef UINT CPUTResult; typedef unsigned int UINT; typedef unsigned long DWORD; // color struct CPUTColor4 { float r; float g; float b; float a; bool operator == (const CPUTColor4& rhs) const { return((rhs.r == r) && (rhs.g == g) && (rhs.b == b) && (rhs.a == a)); } bool operator != (const CPUTColor4& rhs) const { return((rhs.r != r) || (rhs.g != g) || (rhs.b != b) || (rhs.a != a)); } }; // where the loader should start looking from to locate files enum CPUT_PATH_SEARCH_MODE { CPUT_PATH_SEARCH_RESOURCE_DIRECTORY, CPUT_PATH_SEARCH_NONE, }; // string size limitations const UINT CPUT_MAX_PATH = 2048; const UINT CPUT_MAX_STRING_LENGTH = 1024; const UINT CPUT_MAX_SHADER_ERROR_STRING_LENGTH = 8192; const UINT CPUT_MAX_DIGIT_STRING_LENGTH = 5; // Data format types used in interpreting mesh data enum CPUT_DATA_FORMAT_TYPE { CPUT_UNKNOWN=0, CPUT_DOUBLE=1, CPUT_F32=2, CPUT_U64=3, CPUT_I64=4, CPUT_U32=5, CPUT_I32=6, CPUT_U16=7, CPUT_I16=8, CPUT_U8=9, CPUT_I8=10, CPUT_CHAR=11, CPUT_BOOL=12, }; // Corresponding sizes (in bytes) that match CPUT_DATA_FORMAT_TYPE const int CPUT_DATA_FORMAT_SIZE[] = { 0, //CPUT_UNKNOWN=0, 8, //CPUT_DOUBLE, 4, //CPUT_F32, 8, //CPUT_U64, 8, //CPUT_I64, 4, //CPUT_U32, 4, //CPUT_I32, 2, //CPUT_U16, 2, //CPUT_I16, 1, //CPUT_U8, 1, //CPUT_I8, 1, //CPUT_CHAR 1, //CPUT_BOOL }; //----------------------------------------------------------------------------- enum eCPUTMapType { CPUT_MAP_UNDEFINED = 0, CPUT_MAP_READ = 1, CPUT_MAP_WRITE = 2, CPUT_MAP_READ_WRITE = 3, CPUT_MAP_WRITE_DISCARD = 4, CPUT_MAP_NO_OVERWRITE = 5 }; // routines to support unicode + multibyte // TODO: Move to string file //----------------------------------------------------------------------------- #if defined (UNICODE) || defined(_UNICODE) // define string and literal types #define cString std::wstring #define cStringStream std::wstringstream #define cFile std::wfstream #define _L(x) L##x // convert integer to wide/unicode ascii //----------------------------------------------------------------------------- inline std::wstring itoc(const int integer) { wchar_t wcstring[CPUT_MAX_STRING_LENGTH]; swprintf_s(&wcstring[0], CPUT_MAX_STRING_LENGTH, _L("%d"),integer); std::wstring ws(wcstring); return ws; } // convert pointer to wide/unicode ascii //----------------------------------------------------------------------------- inline std::wstring ptoc(const void *pPointer) { std::wstringstream wstream; //std::ostringstream os; wstream << pPointer; std::wstring address; address = wstream.str(); return address; } // convert char* to wide/unicode string //----------------------------------------------------------------------------- inline std::wstring s2ws(const char* stringArg) { // compute the size of the buffer I need to allocate size_t numConvertedChars; mbstowcs_s(&numConvertedChars, NULL, 0, stringArg, _TRUNCATE); numConvertedChars++; // +1 for null termination if(numConvertedChars>CPUT_MAX_STRING_LENGTH) { numConvertedChars = CPUT_MAX_STRING_LENGTH; } // allocate the converted string and copy wchar_t *pWString = new wchar_t[numConvertedChars]; mbstowcs_s(&numConvertedChars, pWString, numConvertedChars, stringArg, _TRUNCATE); std::wstring ws(pWString); delete [] pWString; return ws; /* // alternate method - less 'safe', but possibly useful for unix std::string s = stringArg; std::wstring ws(s.begin(), s.end()); ws.assign(s.begin(), s.end()); return ws; */ } // convert wide/unicode string to char //----------------------------------------------------------------------------- inline char* ws2s(std::wstring string) { size_t numConverted, finalCount; // what size of buffer (in bytes) do we need to allocate for conversion? wcstombs_s(&numConverted, NULL, 0, string.c_str(), CPUT_MAX_STRING_LENGTH); numConverted+=2; // for null termination char *pBuffer = new char[numConverted]; // do the actual conversion wcstombs_s(&finalCount, pBuffer, numConverted, string.c_str(), CPUT_MAX_STRING_LENGTH); return pBuffer; } #else // define string and literal types #define cString std::string #define cStringStream std::stringstream #define cFile std::fstream #define _L(x) x // conversion routine //----------------------------------------------------------------------------- inline std::string s2ws(const char* stringArg) { return std::string(stringArg); } // convert integer to char string //----------------------------------------------------------------------------- inline std::string itoc(const int integer) { char string[CPUT_MAX_STRING_LENGTH]; sprintf_s(string, CPUT_MAX_STRING_LENGTH, "%d",integer); std::string s(string); return s; } // convert pointer to wide/unicode ascii //----------------------------------------------------------------------------- inline std::string ptoc(const void *pPointer) { std::ostringstream stream; stream << pPointer; std::string address; address = stream.str(); return address; } // conversion from ws2s // Doesn't do anything in multibyte version since string is already a char* //----------------------------------------------------------------------------- inline char* ws2s(const char* string) { return const_cast(string); } #endif #ifdef CPUT_FOR_DX11 #include "CPUTRenderTarget.h" #else #error You must supply a target graphics API (ex: #define CPUT_FOR_DX11), or implement the target API for this file. #endif class CPUTCamera; class CPUTRenderStateBlock; // CPUT class //----------------------------------------------------------------------------- class CPUT:public CPUTEventHandler, public CPUTCallbackHandler { protected: CPUTCamera *mpCamera; CPUTCamera *mpShadowCamera; CPUTTimer *mpTimer; float3 mLightColor; // TODO: Get from light(s) float3 mAmbientColor; CPUTBuffer *mpBackBuffer; CPUTBuffer *mpDepthBuffer; CPUTTexture *mpBackBufferTexture; CPUTTexture *mpDepthBufferTexture; public: CPUT() : mpCamera(NULL), mpShadowCamera(NULL), mAmbientColor(0.2f, 0.2f, 0.2f), mLightColor(1.0f, 1.0f, 1.0f), mpBackBuffer(NULL), mpDepthBuffer(NULL), mpBackBufferTexture(NULL), mpDepthBufferTexture(NULL) {} virtual ~CPUT() {} CPUTCamera *GetCamera() { return mpCamera; } CPUTCamera *GetShadowCamera() { return mpShadowCamera; } // TODO: Support more than one. virtual void InnerExecutionLoop() {;} virtual void ResizeWindowSoft(UINT width, UINT height) {UNREFERENCED_PARAMETER(width);UNREFERENCED_PARAMETER(height);} virtual void ResizeWindow(UINT width, UINT height) { CPUTRenderTargetColor::SetActiveWidthHeight( width, height ); CPUTRenderTargetDepth::SetActiveWidthHeight( width, height ); } virtual void DeviceShutdown(){} virtual CPUTEventHandledCode CPUTHandleKeyboardEvent(CPUTKey key) {UNREFERENCED_PARAMETER(key);return CPUT_EVENT_UNHANDLED;} virtual CPUTEventHandledCode CPUTHandleMouseEvent(int x, int y, int wheel, CPUTMouseState state) {UNREFERENCED_PARAMETER(x);UNREFERENCED_PARAMETER(y);UNREFERENCED_PARAMETER(wheel);UNREFERENCED_PARAMETER(state);return CPUT_EVENT_UNHANDLED;} float3 &GetAmbientColor() { return mAmbientColor; } void SetAmbientColor( float3 &ambientColor ) { mAmbientColor = ambientColor; } float3 &GetLightColor() { return mLightColor; } void SetLightColor( float3 &lightColor ) { mLightColor = lightColor; } }; // Include this here to make sure ASSERT resolves correctly #include "CPUTOSServicesWin.h" void CPUTSetDebugName( void *pResource, cString name ); #endif // #ifndef __CPUTBASE_H__ ================================================ FILE: CPUT/CPUT/CPUTAssetLibrary.cpp ================================================ //////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); you may not // use this file except in compliance with the License. You may obtain a copy // of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the // License for the specific language governing permissions and limitations // under the License. //////////////////////////////////////////////////////////////////////////////// #include // for std::string.transform() #include "CPUTAssetLibrary.h" #include "CPUTRenderNode.h" #include "CPUTAssetSet.h" #include "CPUTMaterial.h" #include "CPUTRenderStateBlock.h" #include "CPUTModel.h" #include "CPUTCamera.h" #include "CPUTLight.h" #include "CPUTFont.h" #include "CPUTBuffer.h" // TODO: How do we want to store both DX and OGL assets in the same library? // Could use API-specific libraries. Could move both platforms to single file. etc. // CPUTAssetLibrary *CPUTAssetLibrary::mpAssetLibrary = new CPUTAssetLibraryDX11(); CPUTAssetListEntry *CPUTAssetLibrary::mpAssetSetList = NULL; CPUTAssetListEntry *CPUTAssetLibrary::mpNullNodeList = NULL; CPUTAssetListEntry *CPUTAssetLibrary::mpModelList = NULL; CPUTAssetListEntry *CPUTAssetLibrary::mpCameraList = NULL; CPUTAssetListEntry *CPUTAssetLibrary::mpLightList = NULL; CPUTAssetListEntry *CPUTAssetLibrary::mpMaterialList = NULL; CPUTAssetListEntry *CPUTAssetLibrary::mpTextureList = NULL; CPUTAssetListEntry *CPUTAssetLibrary::mpBufferList = NULL; CPUTAssetListEntry *CPUTAssetLibrary::mpConstantBufferList = NULL; CPUTAssetListEntry *CPUTAssetLibrary::mpRenderStateBlockList = NULL; CPUTAssetListEntry *CPUTAssetLibrary::mpFontList = NULL; //----------------------------------------------------------------------------- void CPUTAssetLibrary::ReleaseTexturesAndBuffers() { CPUTAssetListEntry *pMaterial = mpMaterialList; while( pMaterial ) { ((CPUTMaterial*)pMaterial->pData)->ReleaseTexturesAndBuffers(); pMaterial = pMaterial->pNext; } } //----------------------------------------------------------------------------- void CPUTAssetLibrary::RebindTexturesAndBuffers() { CPUTAssetListEntry *pMaterial = mpMaterialList; while( pMaterial ) { ((CPUTMaterial*)pMaterial->pData)->RebindTexturesAndBuffers(); pMaterial = pMaterial->pNext; } } //----------------------------------------------------------------------------- void CPUTAssetLibrary::DeleteAssetLibrary() { SAFE_DELETE(mpAssetLibrary); } //----------------------------------------------------------------------------- void CPUTAssetLibrary::ReleaseAllLibraryLists() { // Release philosophy: Everyone that references releases. If node refers to parent, then it should release parent, etc... // TODO: Traverse lists. Print names and ref counts (as debug aid) #undef SAFE_RELEASE_LIST #define SAFE_RELEASE_LIST(x) {ReleaseList(x); x = NULL;} SAFE_RELEASE_LIST(mpAssetSetList); SAFE_RELEASE_LIST(mpMaterialList); SAFE_RELEASE_LIST(mpModelList); SAFE_RELEASE_LIST(mpLightList); SAFE_RELEASE_LIST(mpCameraList); SAFE_RELEASE_LIST(mpNullNodeList); SAFE_RELEASE_LIST(mpTextureList ); SAFE_RELEASE_LIST(mpBufferList ); SAFE_RELEASE_LIST(mpConstantBufferList ); SAFE_RELEASE_LIST(mpRenderStateBlockList ); SAFE_RELEASE_LIST(mpFontList); // The following -specific items are destroyed in the derived class // TODO. Move their declaration and definition to the derived class too // SAFE_RELEASE_LIST(mpPixelShaderList); // SAFE_RELEASE_LIST(mpVertexShaderList); // SAFE_RELEASE_LIST(mpGeometryShaderList); } //----------------------------------------------------------------------------- void CPUTAssetLibrary::ReleaseList(CPUTAssetListEntry *pLibraryRoot) { CPUTAssetListEntry *pNext; for( CPUTAssetListEntry *pNodeEntry = pLibraryRoot; NULL != pNodeEntry; pNodeEntry = pNext ) { pNext = pNodeEntry->pNext; CPUTRefCount *pRefCountedNode = (CPUTRefCount*)pNodeEntry->pData; pRefCountedNode->Release(); HEAPCHECK; delete pNodeEntry; } } // Find an asset in a specific library // ** Does not Addref() returned items ** // Asset library doesn't care if we're using absolute paths for names or not, it // just adds/finds/deletes the matching string literal. //----------------------------------------------------------------------------- void *CPUTAssetLibrary::FindAsset(const cString &name, CPUTAssetListEntry *pList, bool nameIsFullPathAndFilename) { cString absolutePathAndFilename; CPUTOSServices *pServices = CPUTOSServices::GetOSServices(); pServices->ResolveAbsolutePathAndFilename( nameIsFullPathAndFilename ? name : (mAssetSetDirectoryName + name), &absolutePathAndFilename); absolutePathAndFilename = nameIsFullPathAndFilename ? name : absolutePathAndFilename; UINT hash = CPUTComputeHash( absolutePathAndFilename ); while(NULL!=pList) { if( hash == pList->hash && (0 == _wcsicmp( absolutePathAndFilename.data(), pList->name.data() )) ) { return (void*)pList->pData; } pList = pList->pNext; } return NULL; } //----------------------------------------------------------------------------- void CPUTAssetLibrary::AddAsset(const cString &name, void *pAsset, CPUTAssetListEntry **pHead) { // convert string to lowercase cString lowercaseName = name; std::transform(lowercaseName.begin(), lowercaseName.end(), lowercaseName.begin(), ::tolower); // Do we already have one by this name? CPUTAssetListEntry *pTail = *pHead; // TODO: Save explicit tail pointer instead of iterating to find the null. for( CPUTAssetListEntry *pCur=*pHead; NULL!=pCur; pCur=pCur->pNext ) { // Assert that we haven't added one with this name ASSERT( 0 != _wcsicmp( pCur->name.data(), name.data() ), _L("Warning: asset ")+name+_L(" already exists") ); pTail = pCur; } CPUTAssetListEntry **pDest = pTail ? &pTail->pNext : pHead; *pDest = new CPUTAssetListEntry(); (*pDest)->hash = CPUTComputeHash(name); (*pDest)->name = name; (*pDest)->pData = pAsset; (*pDest)->pNext = NULL; // TODO: Our assets are not yet all derived from CPUTRenderNode. // TODO: For now, rely on caller performing the AddRef() as it knows the assets type. ((CPUTRefCount*)pAsset)->AddRef(); } //----------------------------------------------------------------------------- CPUTRenderStateBlock *CPUTAssetLibrary::GetRenderStateBlock(const cString &name, bool nameIsFullPathAndFilename ) { // Resolve name to absolute path before searching cString finalName; if( name.at(0) == '$' ) { finalName = name; } else { // Resolve name to absolute path CPUTOSServices *pServices = CPUTOSServices::GetOSServices(); pServices->ResolveAbsolutePathAndFilename( nameIsFullPathAndFilename? name : (mShaderDirectoryName + name), &finalName); } // see if the render state block is already in the library CPUTRenderStateBlock *pRenderStateBlock = FindRenderStateBlock(finalName, true); if(NULL==pRenderStateBlock) { return CPUTRenderStateBlock::CreateRenderStateBlock( name, finalName ); } pRenderStateBlock->AddRef(); return pRenderStateBlock; } //----------------------------------------------------------------------------- CPUTAssetSet *CPUTAssetLibrary::GetAssetSet( const cString &name, bool nameIsFullPathAndFilename ) { // Resolve the absolute path cString absolutePathAndFilename; CPUTOSServices *pServices = CPUTOSServices::GetOSServices(); pServices->ResolveAbsolutePathAndFilename( nameIsFullPathAndFilename ? name : (mAssetSetDirectoryName + name + _L(".set")), &absolutePathAndFilename ); absolutePathAndFilename = nameIsFullPathAndFilename ? name : absolutePathAndFilename; CPUTAssetSet *pAssetSet = FindAssetSet(absolutePathAndFilename, true); if(NULL == pAssetSet) { return CPUTAssetSet::CreateAssetSet( name, absolutePathAndFilename ); } pAssetSet->AddRef(); return pAssetSet; } // TODO: All of these Get() functions look very similar. // Keep them all for their interface, but have them call a common function //----------------------------------------------------------------------------- CPUTMaterial *CPUTAssetLibrary::GetMaterial(const cString &name, bool nameIsFullPathAndFilename, const cString &modelSuffix, const cString &meshSuffix) { // Resolve name to absolute path before searching CPUTOSServices *pServices = CPUTOSServices::GetOSServices(); cString absolutePathAndFilename; pServices->ResolveAbsolutePathAndFilename( nameIsFullPathAndFilename? name : (mMaterialDirectoryName + name + _L(".mtl")), &absolutePathAndFilename); // If we already have one by this name, then return it CPUTMaterial *pMaterial = FindMaterial(absolutePathAndFilename, true); if(NULL==pMaterial) { // We don't already have it in the library, so create it. pMaterial = CPUTMaterial::CreateMaterial( absolutePathAndFilename, modelSuffix, meshSuffix ); return pMaterial; } else if( (0==modelSuffix.length()) && !pMaterial->MaterialRequiresPerModelPayload() ) { // This material doesn't have per-model elements, so we don't need to clone it. pMaterial->AddRef(); return pMaterial; } #ifdef _DEBUG // We need to clone the material. Do that by loading it again, but with a different name. // Add the model's suffix (address as string, plus model's material array index as string) CPUTMaterial *pUniqueMaterial = FindMaterial(absolutePathAndFilename + modelSuffix + meshSuffix, true); ASSERT( NULL == pUniqueMaterial, _L("Unique material already not unique: ") + absolutePathAndFilename + modelSuffix + meshSuffix ); #endif CPUTMaterial *pClonedMaterial = pMaterial->CloneMaterial( absolutePathAndFilename, modelSuffix, meshSuffix ); AddMaterial( absolutePathAndFilename + modelSuffix + meshSuffix, pClonedMaterial ); return pClonedMaterial; } // Get CPUTModel from asset library // If the model exists, then the existing model is Addref'ed and returned //----------------------------------------------------------------------------- CPUTModel *CPUTAssetLibrary::GetModel(const cString &name, bool nameIsFullPathAndFilename) { // Resolve name to absolute path before searching cString absolutePathAndFilename; CPUTOSServices *pServices = CPUTOSServices::GetOSServices(); pServices->ResolveAbsolutePathAndFilename( nameIsFullPathAndFilename? name : (mModelDirectoryName + name + _L(".mdl")), &absolutePathAndFilename); absolutePathAndFilename = nameIsFullPathAndFilename ? name : absolutePathAndFilename; // If we already have one by this name, then return it CPUTModel *pModel = FindModel(absolutePathAndFilename, true); if(NULL!=pModel) { pModel->AddRef(); return pModel; } // Looks like no one calls GetModel(). Or, they never call it for missing models. #if TODO // delegate // Model was not in the library, so create and load a new model pModel = new CPUTModel(); pModel->LoadModelPayload(absolutePathAndFilename); AddModel(name, pModel); return CPUTModel::CreateMode( absolutePathAndFilename, aboslutePathAndFilename ); #endif return pModel; } //----------------------------------------------------------------------------- CPUTCamera *CPUTAssetLibrary::GetCamera(const cString &name) { // TODO: Should we prefix camera names with a path anyway? To keek them unique? // If we already have one by this name, then return it CPUTCamera *pCamera = FindCamera(name, true); if(NULL!=pCamera) { pCamera->AddRef(); return pCamera; } return NULL; } //----------------------------------------------------------------------------- CPUTLight *CPUTAssetLibrary::GetLight(const cString &name) { // If we already have one by this name, then return it CPUTLight *pLight = FindLight(name, true); if(NULL!=pLight) { pLight->AddRef(); return pLight; } return NULL; } //----------------------------------------------------------------------------- CPUTTexture *CPUTAssetLibrary::GetTexture(const cString &name, bool nameIsFullPathAndFilename, bool loadAsSRGB ) { cString finalName; if( name.at(0) == '$' ) { finalName = name; } else { // Resolve name to absolute path CPUTOSServices *pServices = CPUTOSServices::GetOSServices(); pServices->ResolveAbsolutePathAndFilename( nameIsFullPathAndFilename? name : (mTextureDirectoryName + name), &finalName); } // If we already have one by this name, then return it CPUTTexture *pTexture = FindTexture(finalName, true); if(NULL==pTexture) { return CPUTTexture::CreateTexture( name, finalName, loadAsSRGB); } pTexture->AddRef(); return pTexture; } //----------------------------------------------------------------------------- CPUTBuffer *CPUTAssetLibrary::GetBuffer(const cString &name ) { // If we already have one by this name, then return it CPUTBuffer *pBuffer = FindBuffer(name, true); ASSERT( pBuffer, _L("Can't find buffer ") + name ); pBuffer->AddRef(); return pBuffer; } //----------------------------------------------------------------------------- CPUTBuffer *CPUTAssetLibrary::GetConstantBuffer(const cString &name ) { // If we already have one by this name, then return it CPUTBuffer *pBuffer = FindConstantBuffer(name, true); ASSERT( pBuffer, _L("Can't find constant buffer ") + name ); pBuffer->AddRef(); return pBuffer; } //----------------------------------------------------------------------------- CPUTFont *CPUTAssetLibrary::GetFont(const cString &name ) { // Resolve name to absolute path CPUTOSServices *pServices = CPUTOSServices::GetOSServices(); cString absolutePathAndFilename; pServices->ResolveAbsolutePathAndFilename( (mFontDirectoryName + name), &absolutePathAndFilename); // If we already have one by this name, then return it CPUTFont *pFont = FindFont(absolutePathAndFilename, true); if(NULL==pFont) { return CPUTFont::CreateFont( name, absolutePathAndFilename); } pFont->AddRef(); return pFont; } ================================================ FILE: CPUT/CPUT/CPUTAssetLibrary.h ================================================ //////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); you may not // use this file except in compliance with the License. You may obtain a copy // of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the // License for the specific language governing permissions and limitations // under the License. //////////////////////////////////////////////////////////////////////////////// #ifndef __CPUTASSETLIBRARY_H__ #define __CPUTASSETLIBRARY_H__ #include "CPUT.h" #include "CPUTOSServicesWin.h" // TODO: why is this windows-specific? // Global Asset Library // // The purpose of this library is to keep a copy of all loaded assets and // provide a one-stop-loading system. All assets that are loaded into the // system via the Getxxx() operators stays in the library. Further Getxxx() // operations on an already loaded object will addref and return the previously // loaded object. //----------------------------------------------------------------------------- // node that holds a single library object struct CPUTAssetListEntry { UINT hash; cString name; void *pData; CPUTAssetListEntry *pNext; }; #define SAFE_RELEASE_LIST(list) ReleaseList(list);(list)=NULL; class CPUTAssetSet; class CPUTNullNode; class CPUTModel; class CPUTMaterial; class CPUTLight; class CPUTCamera; class CPUTTexture; class CPUTBuffer; class CPUTRenderStateBlock; class CPUTFont; class CPUTAssetLibrary { protected: static CPUTAssetLibrary *mpAssetLibrary; // simple linked lists for now, but if we want to optimize or load blocks // we can change these to dynamically re-sizing arrays and then just do // memcopies into the structs. // Note: No camera, light, or NullNode directory names - they don't have payload files (i.e., they are defined completely in the .set file) cString mAssetSetDirectoryName; cString mModelDirectoryName; cString mMaterialDirectoryName; cString mTextureDirectoryName; cString mShaderDirectoryName; cString mFontDirectoryName; public: // TODO: temporary for debug. // TODO: Make these lists static. Share assets (e.g., texture) across all requests for this process. static CPUTAssetListEntry *mpAssetSetList; static CPUTAssetListEntry *mpNullNodeList; static CPUTAssetListEntry *mpModelList; static CPUTAssetListEntry *mpCameraList; static CPUTAssetListEntry *mpLightList; static CPUTAssetListEntry *mpMaterialList; static CPUTAssetListEntry *mpTextureList; static CPUTAssetListEntry *mpBufferList; static CPUTAssetListEntry *mpConstantBufferList; static CPUTAssetListEntry *mpRenderStateBlockList; static CPUTAssetListEntry *mpFontList; static void RebindTexturesAndBuffers(); static void ReleaseTexturesAndBuffers(); public: static CPUTAssetLibrary *GetAssetLibrary(){ return mpAssetLibrary; } static void DeleteAssetLibrary(); CPUTAssetLibrary() {} virtual ~CPUTAssetLibrary() {} // Add/get/delete items to specified library void *FindAsset(const cString &name, CPUTAssetListEntry *pList, bool nameIsFullPathAndFilename=false); virtual void ReleaseAllLibraryLists(); void SetMediaDirectoryName( const cString &directoryName) { mAssetSetDirectoryName = directoryName + _L("Asset\\"); mModelDirectoryName = directoryName + _L("Asset\\"); mMaterialDirectoryName = directoryName + _L("Material\\"); mTextureDirectoryName = directoryName + _L("Texture\\"); mShaderDirectoryName = directoryName + _L("Shader\\"); // mFontStateBlockDirectoryName = directoryName + _L("Font\\"); } void SetAssetSetDirectoryName( const cString &directoryName) { mAssetSetDirectoryName = directoryName; } void SetModelDirectoryName( const cString &directoryName) { mModelDirectoryName = directoryName; } void SetMaterialDirectoryName( const cString &directoryName) { mMaterialDirectoryName = directoryName; } void SetTextureDirectoryName( const cString &directoryName) { mTextureDirectoryName = directoryName; } void SetShaderDirectoryName( const cString &directoryName) { mShaderDirectoryName = directoryName; } void SetFontDirectoryName( const cString &directoryName) { mFontDirectoryName = directoryName; } void SetAllAssetDirectoryNames( const cString &directoryName) { mAssetSetDirectoryName = directoryName; mModelDirectoryName = directoryName; mMaterialDirectoryName = directoryName; mTextureDirectoryName = directoryName; mShaderDirectoryName = directoryName; mFontDirectoryName = directoryName; }; cString &GetAssetSetDirectoryName() { return mAssetSetDirectoryName; } cString &GetModelDirectory() { return mModelDirectoryName; } cString &GetMaterialDirectory() { return mMaterialDirectoryName; } cString &GetTextureDirectory() { return mTextureDirectoryName; } cString &GetShaderDirectory() { return mShaderDirectoryName; } cString &GetFontDirectory() { return mFontDirectoryName; } void AddAssetSet( const cString &name, CPUTAssetSet *pAssetSet) { AddAsset( name, pAssetSet, &mpAssetSetList ); } void AddNullNode( const cString &name, CPUTNullNode *pNullNode) { AddAsset( name, pNullNode, &mpNullNodeList ); } void AddModel( const cString &name, CPUTModel *pModel) { AddAsset( name, pModel, &mpModelList ); } void AddMaterial( const cString &name, CPUTMaterial *pMaterial) { AddAsset( name, pMaterial, &mpMaterialList ); } void AddLight( const cString &name, CPUTLight *pLight) { AddAsset( name, pLight, &mpLightList ); } void AddCamera( const cString &name, CPUTCamera *pCamera) { AddAsset( name, pCamera, &mpCameraList ); } void AddTexture( const cString &name, CPUTTexture *pTexture) { AddAsset( name, pTexture, &mpTextureList ); } void AddBuffer( const cString &name, CPUTBuffer *pBuffer) { AddAsset( name, pBuffer, &mpBufferList ); } void AddConstantBuffer( const cString &name, CPUTBuffer *pBuffer) { AddAsset( name, pBuffer, &mpConstantBufferList ); } void AddRenderStateBlock(const cString &name, CPUTRenderStateBlock *pRenderStateBlock){ AddAsset( name, pRenderStateBlock, &mpRenderStateBlockList ); } void AddFont( const cString &name, CPUTFont *pFont) { AddAsset( name, pFont, &mpFontList ); } CPUTAssetSet *FindAssetSet(const cString &name, bool nameIsFullPathAndFilename=false) { return (CPUTAssetSet*)FindAsset( name, mpAssetSetList, nameIsFullPathAndFilename ); } CPUTNullNode *FindNullNode(const cString &name, bool nameIsFullPathAndFilename=false) { return (CPUTNullNode*)FindAsset( name, mpNullNodeList, nameIsFullPathAndFilename ); } CPUTModel *FindModel(const cString &name, bool nameIsFullPathAndFilename=false) { return (CPUTModel*)FindAsset( name, mpModelList, nameIsFullPathAndFilename ); } CPUTMaterial *FindMaterial(const cString &name, bool nameIsFullPathAndFilename=false) { return (CPUTMaterial*)FindAsset( name, mpMaterialList, nameIsFullPathAndFilename ); } CPUTLight *FindLight(const cString &name, bool nameIsFullPathAndFilename=false) { return (CPUTLight*)FindAsset( name, mpLightList, nameIsFullPathAndFilename ); } CPUTCamera *FindCamera(const cString &name, bool nameIsFullPathAndFilename=false) { return (CPUTCamera*)FindAsset( name, mpCameraList, nameIsFullPathAndFilename ); } CPUTTexture *FindTexture(const cString &name, bool nameIsFullPathAndFilename=false) { return (CPUTTexture*)FindAsset( name, mpTextureList, nameIsFullPathAndFilename ); } CPUTBuffer *FindBuffer(const cString &name, bool nameIsFullPathAndFilename=false) { return (CPUTBuffer*)FindAsset( name, mpBufferList, nameIsFullPathAndFilename ); } CPUTBuffer *FindConstantBuffer(const cString &name, bool nameIsFullPathAndFilename=false) { return (CPUTBuffer*)FindAsset( name, mpConstantBufferList, nameIsFullPathAndFilename ); } CPUTRenderStateBlock *FindRenderStateBlock(const cString &name, bool nameIsFullPathAndFilename=false ) { return (CPUTRenderStateBlock*)FindAsset( name, mpRenderStateBlockList, nameIsFullPathAndFilename ); } CPUTFont *FindFont(const cString &name, bool nameIsFullPathAndFilename=false) { return (CPUTFont*)FindAsset( name, mpFontList, nameIsFullPathAndFilename ); } // If the asset exists, these 'Get' methods will addref and return it. Otherwise, // they will create it and return it. CPUTAssetSet *GetAssetSet( const cString &name, bool nameIsFullPathAndFilename=false ); CPUTTexture *GetTexture( const cString &name, bool nameIsFullPathAndFilename=false, bool loadAsSRGB=true ); CPUTMaterial *GetMaterial( const cString &name, bool nameIsFullPathAndFilename=false, const cString &modelSuffix=_L(""), const cString &meshSuffix=_L("") ); CPUTModel *GetModel( const cString &name, bool nameIsFullPathAndFilename=false ); CPUTRenderStateBlock *GetRenderStateBlock(const cString &name, bool nameIsFullPathAndFilename=false); CPUTBuffer *GetBuffer( const cString &name ); CPUTBuffer *GetConstantBuffer( const cString &name ); CPUTCamera *GetCamera( const cString &name ); CPUTLight *GetLight( const cString &name ); CPUTFont *GetFont( const cString &name ); protected: // helper functions void ReleaseList(CPUTAssetListEntry *pLibraryRoot); void AddAsset( const cString &name, void *pAsset, CPUTAssetListEntry **pHead ); UINT CPUTComputeHash( const cString &string ) { size_t length = string.length(); UINT hash = 0; for( size_t ii=0; iipData))->Release(); pOldNode = pNode; pNode = pNode->pNext; delete pOldNode; } HEAPCHECK; } // Retrieve specified pixel shader //----------------------------------------------------------------------------- CPUTResult CPUTAssetLibraryDX11::GetPixelShader( const cString &name, ID3D11Device *pD3dDevice, const cString &shaderMain, const cString &shaderProfile, CPUTPixelShaderDX11 **ppPixelShader, bool nameIsFullPathAndFilename ) { CPUTResult result = CPUT_SUCCESS; cString finalName; if( name.at(0) == '$' ) { finalName = name; } else { // Resolve name to absolute path CPUTOSServices *pServices = CPUTOSServices::GetOSServices(); pServices->ResolveAbsolutePathAndFilename( nameIsFullPathAndFilename? name : (mShaderDirectoryName + name), &finalName); } // see if the shader is already in the library void *pShader = FindPixelShader(finalName + shaderMain + shaderProfile, true); if(NULL!=pShader) { *ppPixelShader = (CPUTPixelShaderDX11*) pShader; (*ppPixelShader)->AddRef(); return result; } *ppPixelShader = CPUTPixelShaderDX11::CreatePixelShader( finalName, pD3dDevice, shaderMain, shaderProfile ); return result; } // Retrieve specified pixel shader //----------------------------------------------------------------------------- CPUTResult CPUTAssetLibraryDX11::GetComputeShader( const cString &name, ID3D11Device *pD3dDevice, const cString &shaderMain, const cString &shaderProfile, CPUTComputeShaderDX11 **ppComputeShader, bool nameIsFullPathAndFilename ) { CPUTResult result = CPUT_SUCCESS; cString finalName; if( name.at(0) == '$' ) { finalName = name; } else { // Resolve name to absolute path CPUTOSServices* pServices = CPUTOSServices::GetOSServices(); pServices->ResolveAbsolutePathAndFilename( nameIsFullPathAndFilename? name : (mShaderDirectoryName + name), &finalName); } // see if the shader is already in the library void *pShader = FindComputeShader(finalName + shaderMain + shaderProfile, true); if(NULL!=pShader) { *ppComputeShader = (CPUTComputeShaderDX11*) pShader; (*ppComputeShader)->AddRef(); return result; } *ppComputeShader = CPUTComputeShaderDX11::CreateComputeShader( finalName, pD3dDevice, shaderMain, shaderProfile ); return result; } // Retrieve specified vertex shader //----------------------------------------------------------------------------- CPUTResult CPUTAssetLibraryDX11::GetVertexShader( const cString &name, ID3D11Device *pD3dDevice, const cString &shaderMain, const cString &shaderProfile, CPUTVertexShaderDX11 **ppVertexShader, bool nameIsFullPathAndFilename ) { CPUTResult result = CPUT_SUCCESS; cString finalName; if( name.at(0) == '$' ) { finalName = name; } else { // Resolve name to absolute path CPUTOSServices *pServices = CPUTOSServices::GetOSServices(); pServices->ResolveAbsolutePathAndFilename( nameIsFullPathAndFilename? name : (mShaderDirectoryName + name), &finalName); } // see if the shader is already in the library void *pShader = FindVertexShader(finalName + shaderMain + shaderProfile, true); if(NULL!=pShader) { *ppVertexShader = (CPUTVertexShaderDX11*) pShader; (*ppVertexShader)->AddRef(); return result; } *ppVertexShader = CPUTVertexShaderDX11::CreateVertexShader( finalName, pD3dDevice, shaderMain, shaderProfile ); return result; } // Retrieve specified geometry shader //----------------------------------------------------------------------------- CPUTResult CPUTAssetLibraryDX11::GetGeometryShader( const cString &name, ID3D11Device *pD3dDevice, const cString &shaderMain, const cString &shaderProfile, CPUTGeometryShaderDX11 **ppGeometryShader, bool nameIsFullPathAndFilename ) { CPUTResult result = CPUT_SUCCESS; cString finalName; if( name.at(0) == '$' ) { finalName = name; } else { // Resolve name to absolute path CPUTOSServices *pServices = CPUTOSServices::GetOSServices(); pServices->ResolveAbsolutePathAndFilename( nameIsFullPathAndFilename? name : (mShaderDirectoryName + name), &finalName); } // see if the shader is already in the library void *pShader = FindGeometryShader(finalName + shaderMain + shaderProfile, true); if(NULL!=pShader) { *ppGeometryShader = (CPUTGeometryShaderDX11*) pShader; (*ppGeometryShader)->AddRef(); return result; } *ppGeometryShader = CPUTGeometryShaderDX11::CreateGeometryShader( finalName, pD3dDevice, shaderMain, shaderProfile ); return result; } // Retrieve specified hull shader //----------------------------------------------------------------------------- CPUTResult CPUTAssetLibraryDX11::GetHullShader( const cString &name, ID3D11Device *pD3dDevice, const cString &shaderMain, const cString &shaderProfile, CPUTHullShaderDX11 **ppHullShader, bool nameIsFullPathAndFilename ) { CPUTResult result = CPUT_SUCCESS; cString finalName; if( name.at(0) == '$' ) { finalName = name; } else { // Resolve name to absolute path CPUTOSServices *pServices = CPUTOSServices::GetOSServices(); pServices->ResolveAbsolutePathAndFilename( nameIsFullPathAndFilename? name : (mShaderDirectoryName + name), &finalName); } // see if the shader is already in the library void *pShader = FindHullShader(finalName + shaderMain + shaderProfile, true); if(NULL!=pShader) { *ppHullShader = (CPUTHullShaderDX11*) pShader; (*ppHullShader)->AddRef(); return result; } *ppHullShader = CPUTHullShaderDX11::CreateHullShader( finalName, pD3dDevice, shaderMain, shaderProfile ); return result; } // Retrieve specified domain shader //----------------------------------------------------------------------------- CPUTResult CPUTAssetLibraryDX11::GetDomainShader( const cString &name, ID3D11Device *pD3dDevice, const cString &shaderMain, const cString &shaderProfile, CPUTDomainShaderDX11 **ppDomainShader, bool nameIsFullPathAndFilename ) { CPUTResult result = CPUT_SUCCESS; cString finalName; if( name.at(0) == '$' ) { finalName = name; } else { // Resolve name to absolute path CPUTOSServices *pServices = CPUTOSServices::GetOSServices(); pServices->ResolveAbsolutePathAndFilename( nameIsFullPathAndFilename? name : (mShaderDirectoryName + name), &finalName); } // see if the shader is already in the library void *pShader = FindDomainShader(finalName + shaderMain + shaderProfile, true); if(NULL!=pShader) { *ppDomainShader = (CPUTDomainShaderDX11*) pShader; (*ppDomainShader)->AddRef(); return result; } *ppDomainShader = CPUTDomainShaderDX11::CreateDomainShader( finalName, pD3dDevice, shaderMain, shaderProfile ); return result; } //----------------------------------------------------------------------------- CPUTResult CPUTAssetLibraryDX11::CreatePixelShaderFromMemory( const cString &name, ID3D11Device *pD3dDevice, const cString &shaderMain, const cString &shaderProfile, CPUTPixelShaderDX11 **ppShader, char *pShaderSource ) { CPUTResult result = CPUT_SUCCESS; void *pShader = FindPixelShader(name + shaderMain + shaderProfile, true); ASSERT( NULL == pShader, _L("Shader already exists.") ); *ppShader = CPUTPixelShaderDX11::CreatePixelShaderFromMemory( name, pD3dDevice, shaderMain, shaderProfile, pShaderSource); return result; } //----------------------------------------------------------------------------- CPUTResult CPUTAssetLibraryDX11::CreateVertexShaderFromMemory( const cString &name, ID3D11Device *pD3dDevice, const cString &shaderMain, const cString &shaderProfile, CPUTVertexShaderDX11 **ppShader, char *pShaderSource ) { CPUTResult result = CPUT_SUCCESS; void *pShader = FindPixelShader(name + shaderMain + shaderProfile, true); ASSERT( NULL == pShader, _L("Shader already exists.") ); *ppShader = CPUTVertexShaderDX11::CreateVertexShaderFromMemory( name, pD3dDevice, shaderMain, shaderProfile, pShaderSource); return result; } //----------------------------------------------------------------------------- CPUTResult CPUTAssetLibraryDX11::CreateComputeShaderFromMemory( const cString &name, ID3D11Device *pD3dDevice, const cString &shaderMain, const cString &shaderProfile, CPUTComputeShaderDX11 **ppShader, char *pShaderSource ) { CPUTResult result = CPUT_SUCCESS; void *pShader = FindPixelShader(name + shaderMain + shaderProfile, true); ASSERT( NULL == pShader, _L("Shader already exists.") ); *ppShader = CPUTComputeShaderDX11::CreateComputeShaderFromMemory( name, pD3dDevice, shaderMain, shaderProfile, pShaderSource); return result; } // Use DX11 compile from file method to do all the heavy lifting //----------------------------------------------------------------------------- CPUTResult CPUTAssetLibraryDX11::CompileShaderFromFile( const cString &fileName, const cString &shaderMain, const cString &shaderProfile, ID3DBlob **ppBlob ) { CPUTResult result = CPUT_SUCCESS; char pShaderMainAsChar[128]; char pShaderProfileAsChar[128]; ASSERT( shaderMain.length() < 128, _L("Shader main name '") + shaderMain + _L("' longer than 128 chars.") ); ASSERT( shaderProfile.length() < 128, _L("Shader profile name '") + shaderProfile + _L("' longer than 128 chars.") ); size_t count; wcstombs_s( &count, pShaderMainAsChar, shaderMain.c_str(), 128 ); wcstombs_s( &count, pShaderProfileAsChar, shaderProfile.c_str(), 128 ); // use DirectX to compile the shader file ID3DBlob *pErrorBlob = NULL; D3D10_SHADER_MACRO pShaderMacros[2] = { "_CPUT", "1", NULL, NULL }; HRESULT hr = D3DX11CompileFromFile( fileName.c_str(), // fileName pShaderMacros, // macro define's NULL, // includes pShaderMainAsChar, // main function name pShaderProfileAsChar, // shader profile/feature level 0, // flags 1 0, // flags 2 NULL, // threaded load? (no for right now) ppBlob, // blob data with compiled code &pErrorBlob, // any compile errors stored here NULL ); ASSERT( SUCCEEDED(hr), _L("Error compiling shader '") + fileName + _L("'.\n") + (pErrorBlob ? s2ws((char*)pErrorBlob->GetBufferPointer()) : _L("no error message") ) ); if(pErrorBlob) { pErrorBlob->Release(); } return result; } // Use DX11 compile from file method to do all the heavy lifting //----------------------------------------------------------------------------- CPUTResult CPUTAssetLibraryDX11::CompileShaderFromMemory( const char *pShaderSource, const cString &shaderMain, const cString &shaderProfile, ID3DBlob **ppBlob ) { CPUTResult result = CPUT_SUCCESS; char pShaderMainAsChar[128]; char pShaderProfileAsChar[128]; ASSERT( shaderMain.length() < 128, _L("Shader main name '") + shaderMain + _L("' longer than 128 chars.") ); ASSERT( shaderProfile.length() < 128, _L("Shader profile name '") + shaderProfile + _L("' longer than 128 chars.") ); size_t count; wcstombs_s( &count, pShaderMainAsChar, shaderMain.c_str(), 128 ); wcstombs_s( &count, pShaderProfileAsChar, shaderProfile.c_str(), 128 ); // use DirectX to compile the shader file ID3DBlob *pErrorBlob = NULL; D3D10_SHADER_MACRO pShaderMacros[2] = { "_CPUT", "1", NULL, NULL }; // TODO: Support passed-in, and defined in .mtl file. Perhaps under [Shader Defines], etc char *pShaderMainAsChars = ws2s(shaderMain.c_str()); HRESULT hr = D3DX11CompileFromMemory( pShaderSource, // shader as a string strlen( pShaderSource ), // pShaderMainAsChars, // Use entrypoint as file name pShaderMacros, // macro define's NULL, // includes pShaderMainAsChar, // main function name pShaderProfileAsChar, // shader profile/feature level 0, // flags 1 0, // flags 2 NULL, // threaded load? (no for right now) ppBlob, // blob data with compiled code &pErrorBlob, // any compile errors stored here NULL ); ASSERT( SUCCEEDED(hr), _L("Error compiling shader '") + shaderMain + _L("'.\n") + (pErrorBlob ? s2ws((char*)pErrorBlob->GetBufferPointer()) : _L("no error message") ) ); if(pErrorBlob) { pErrorBlob->Release(); } delete pShaderMainAsChars; return result; } ================================================ FILE: CPUT/CPUT/CPUTAssetLibraryDX11.h ================================================ //////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); you may not // use this file except in compliance with the License. You may obtain a copy // of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the // License for the specific language governing permissions and limitations // under the License. //////////////////////////////////////////////////////////////////////////////// #ifndef __CPUTASSETLIBRARYDX11_H__ #define __CPUTASSETLIBRARYDX11_H__ #include "CPUTAssetLibrary.h" #include "CPUTConfigBlock.h" #include #include // for D3DX11_IMAGE_LOAD_INFO structs class CPUTAssetSet; class CPUTMaterial; class CPUTModel; class CPUTNullNode; class CPUTCamera; class CPUTRenderStateBlock; class CPUTLight; class CPUTTexture; class CPUTVertexShaderDX11; class CPUTPixelShaderDX11; class CPUTComputeShaderDX11; class CPUTGeometryShaderDX11; class CPUTHullShaderDX11; class CPUTDomainShaderDX11; //----------------------------------------------------------------------------- struct CPUTSRGBLoadFlags { bool bInterpretInputasSRGB; bool bWritetoSRGBOutput; }; //----------------------------------------------------------------------------- class CPUTAssetLibraryDX11:public CPUTAssetLibrary { protected: static CPUTAssetListEntry *mpPixelShaderList; static CPUTAssetListEntry *mpComputeShaderList; static CPUTAssetListEntry *mpVertexShaderList; static CPUTAssetListEntry *mpGeometryShaderList; static CPUTAssetListEntry *mpHullShaderList; static CPUTAssetListEntry *mpDomainShaderList; public: CPUTAssetLibraryDX11(){} virtual ~CPUTAssetLibraryDX11() { ReleaseAllLibraryLists(); } virtual void ReleaseAllLibraryLists(); void ReleaseIunknownList( CPUTAssetListEntry *pList ); void AddPixelShader( const cString &name, CPUTPixelShaderDX11 *pShader) { AddAsset( name, pShader, &mpPixelShaderList ); } void AddComputeShader( const cString &name, CPUTComputeShaderDX11 *pShader) { AddAsset( name, pShader, &mpComputeShaderList ); } void AddVertexShader( const cString &name, CPUTVertexShaderDX11 *pShader) { AddAsset( name, pShader, &mpVertexShaderList ); } void AddGeometryShader( const cString &name, CPUTGeometryShaderDX11 *pShader) { AddAsset( name, pShader, &mpGeometryShaderList ); } void AddHullShader( const cString &name, CPUTHullShaderDX11 *pShader) { AddAsset( name, pShader, &mpHullShaderList ); } void AddDomainShader( const cString &name, CPUTDomainShaderDX11 *pShader) { AddAsset( name, pShader, &mpDomainShaderList ); } CPUTPixelShaderDX11 *FindPixelShader( const cString &name, bool nameIsFullPathAndFilename=false ) { return (CPUTPixelShaderDX11*)FindAsset( name, mpPixelShaderList, nameIsFullPathAndFilename ); } CPUTComputeShaderDX11 *FindComputeShader( const cString &name, bool nameIsFullPathAndFilename=false ) { return (CPUTComputeShaderDX11*)FindAsset( name, mpComputeShaderList, nameIsFullPathAndFilename ); } CPUTVertexShaderDX11 *FindVertexShader( const cString &name, bool nameIsFullPathAndFilename=false ) { return (CPUTVertexShaderDX11*)FindAsset( name, mpVertexShaderList, nameIsFullPathAndFilename ); } CPUTGeometryShaderDX11 *FindGeometryShader( const cString &name, bool nameIsFullPathAndFilename=false ) { return (CPUTGeometryShaderDX11*)FindAsset( name, mpGeometryShaderList, nameIsFullPathAndFilename ); } CPUTHullShaderDX11 *FindHullShader( const cString &name, bool nameIsFullPathAndFilename=false ) { return (CPUTHullShaderDX11*)FindAsset( name, mpHullShaderList, nameIsFullPathAndFilename ); } CPUTDomainShaderDX11 *FindDomainShader( const cString &name, bool nameIsFullPathAndFilename=false ) { return (CPUTDomainShaderDX11*)FindAsset( name, mpDomainShaderList, nameIsFullPathAndFilename ); } // shaders - vertex, pixel CPUTResult GetPixelShader( const cString &name, ID3D11Device *pD3dDevice, const cString &shaderMain, const cString &shaderProfile, CPUTPixelShaderDX11 **ppShader, bool nameIsFullPathAndFilename=false); CPUTResult GetComputeShader( const cString &name, ID3D11Device *pD3dDevice, const cString &shaderMain, const cString &shaderProfile, CPUTComputeShaderDX11 **ppShader, bool nameIsFullPathAndFilename=false); CPUTResult GetVertexShader( const cString &name, ID3D11Device *pD3dDevice, const cString &shaderMain, const cString &shaderProfile, CPUTVertexShaderDX11 **ppShader, bool nameIsFullPathAndFilename=false); CPUTResult GetGeometryShader( const cString &name, ID3D11Device *pD3dDevice, const cString &shaderMain, const cString &shaderProfile, CPUTGeometryShaderDX11 **ppShader, bool nameIsFullPathAndFilename=false); CPUTResult GetHullShader( const cString &name, ID3D11Device *pD3dDevice, const cString &shaderMain, const cString &shaderProfile, CPUTHullShaderDX11 **ppShader, bool nameIsFullPathAndFilename=false); CPUTResult GetDomainShader( const cString &name, ID3D11Device *pD3dDevice, const cString &shaderMain, const cString &shaderProfile, CPUTDomainShaderDX11 **ppShader, bool nameIsFullPathAndFilename=false); // shaders - vertex, pixel CPUTResult CreatePixelShaderFromMemory( const cString &name, ID3D11Device *pD3dDevice, const cString &shaderMain, const cString &shaderProfile, CPUTPixelShaderDX11 **ppShader, char *pShaderSource ); CPUTResult CreateComputeShaderFromMemory( const cString &name, ID3D11Device *pD3dDevice, const cString &shaderMain, const cString &shaderProfile, CPUTComputeShaderDX11 **ppShader, char *pShaderSource ); CPUTResult CreateVertexShaderFromMemory( const cString &name, ID3D11Device *pD3dDevice, const cString &shaderMain, const cString &shaderProfile, CPUTVertexShaderDX11 **ppShader, char *pShaderSource ); CPUTResult CreateGeometryShaderFromMemory( const cString &name, ID3D11Device *pD3dDevice, const cString &shaderMain, const cString &shaderProfile, CPUTGeometryShaderDX11 **ppShader, char *pShaderSource ); CPUTResult CreateHullShaderFromMemory( const cString &name, ID3D11Device *pD3dDevice, const cString &shaderMain, const cString &shaderProfile, CPUTHullShaderDX11 **ppShader, char *pShaderSource ); CPUTResult CreateDomainShaderFromMemory( const cString &name, ID3D11Device *pD3dDevice, const cString &shaderMain, const cString &shaderProfile, CPUTDomainShaderDX11 **ppShader, char *pShaderSource ); CPUTResult CompileShaderFromFile( const cString &fileName, const cString &shaderMain, const cString &shaderProfile, ID3DBlob **ppBlob); CPUTResult CompileShaderFromMemory(const char *pShaderSource, const cString &shaderMain, const cString &shaderProfile, ID3DBlob **ppBlob); }; #endif // #ifndef __CPUTASSETLIBRARYDX11_H__ ================================================ FILE: CPUT/CPUT/CPUTAssetSet.cpp ================================================ //////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); you may not // use this file except in compliance with the License. You may obtain a copy // of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the // License for the specific language governing permissions and limitations // under the License. //////////////////////////////////////////////////////////////////////////////// #include "CPUTAssetSet.h" #ifdef CPUT_FOR_DX11 #include "CPUTAssetLibraryDX11.h" #else #error You must supply a target graphics API (ex: #define CPUT_FOR_DX11), or implement the target API for this file. #endif //----------------------------------------------------------------------------- CPUTAssetSet::CPUTAssetSet() : mppAssetList(NULL), mAssetCount(0), mpRootNode(NULL), mpFirstCamera(NULL), mCameraCount(0) { } //----------------------------------------------------------------------------- CPUTAssetSet::~CPUTAssetSet() { SAFE_RELEASE(mpFirstCamera); // Deleteing the asset set implies recursively releasing all the assets in the hierarchy if(mpRootNode && !mpRootNode->ReleaseRecursive() ) { mpRootNode = NULL; } } //----------------------------------------------------------------------------- void CPUTAssetSet::RenderRecursive(CPUTRenderParameters &renderParams ) { if(mpRootNode) { mpRootNode->RenderRecursive(renderParams); } } //----------------------------------------------------------------------------- void CPUTAssetSet::RenderShadowRecursive(CPUTRenderParameters &renderParams ) { if(mpRootNode) { mpRootNode->RenderShadowRecursive(renderParams); } } //----------------------------------------------------------------------------- void CPUTAssetSet::GetBoundingBox(float3 *pCenter, float3 *pHalf) { *pCenter = *pHalf = float3(0.0f); if(mpRootNode) { mpRootNode->GetBoundingBoxRecursive(pCenter, pHalf); } } //----------------------------------------------------------------------------- CPUTResult CPUTAssetSet::GetAssetByIndex(const UINT index, CPUTRenderNode **ppRenderNode) { ASSERT( NULL != ppRenderNode, _L("Invalid NULL parameter") ); *ppRenderNode = mppAssetList[index]; mppAssetList[index]->AddRef(); return CPUT_SUCCESS; } // Note: We create an object of derived type here. What do we want to do when we also support OGL? Have DX and OGL specific Create functions? #include "CPUTAssetSetDX11.h" //----------------------------------------------------------------------------- CPUTAssetSet *CPUTAssetSet::CreateAssetSet( const cString &name, const cString &absolutePathAndFilename ) { // TODO: accept DX11/OGL param to control which platform we generate. // TODO: be sure to support the case where we want to support only one of them #ifdef CPUT_FOR_DX11 return CPUTAssetSetDX11::CreateAssetSet( name, absolutePathAndFilename ); #else #error You must supply a target graphics API (ex: #define CPUT_FOR_DX11), or implement the target API for this file. #endif } ================================================ FILE: CPUT/CPUT/CPUTAssetSet.h ================================================ //////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); you may not // use this file except in compliance with the License. You may obtain a copy // of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the // License for the specific language governing permissions and limitations // under the License. //////////////////////////////////////////////////////////////////////////////// #ifndef __CPUTASSETSET_H__ #define __CPUTASSETSET_H__ #include "CPUTRefCount.h" #include "CPUTNullNode.h" #include "CPUTCamera.h" class CPUTRenderNode; class CPUTNullNode; class CPUTRenderParameters; // initial size and growth defines class CPUTAssetSet : public CPUTRefCount { protected: CPUTRenderNode **mppAssetList; UINT mAssetCount; CPUTNullNode *mpRootNode; CPUTCamera *mpFirstCamera; UINT mCameraCount; ~CPUTAssetSet(); // Destructor is not public. Must release instead of delete. public: static CPUTAssetSet *CreateAssetSet( const cString &name, const cString &absolutePathAndFilename ); CPUTAssetSet::CPUTAssetSet(); UINT GetAssetCount() { return mAssetCount; } UINT GetCameraCount() { return mCameraCount; } CPUTResult GetAssetByIndex(const UINT index, CPUTRenderNode **ppRenderNode); CPUTRenderNode *GetRoot() { if(mpRootNode){mpRootNode->AddRef();} return mpRootNode; } void SetRoot( CPUTNullNode *pRoot) { SAFE_RELEASE(mpRootNode); mpRootNode = pRoot; } CPUTCamera *GetFirstCamera() { if(mpFirstCamera){mpFirstCamera->AddRef();} return mpFirstCamera; } // TODO: Consider supporting indexed access to each asset type void RenderRecursive(CPUTRenderParameters &renderParams); void RenderShadowRecursive(CPUTRenderParameters &renderParams); void UpdateRecursive( float deltaSeconds ); virtual CPUTResult LoadAssetSet(cString name) = 0; void GetBoundingBox(float3 *pCenter, float3 *pHalf); }; #endif // #ifndef __CPUTASSETSET_H__ ================================================ FILE: CPUT/CPUT/CPUTAssetSetDX11.cpp ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #include "CPUTAssetSetDX11.h" #include "CPUTModelDX11.h" #include "CPUTAssetLibraryDX11.h" #include "CPUTCamera.h" #include "CPUTLight.h" //----------------------------------------------------------------------------- CPUTAssetSetDX11::~CPUTAssetSetDX11() { // Release all the elements in the asset list. Note that we don't // recursively delete the hierarchy here. // We release the entries here because this class is where we add them. // TODO: Howevere, all derivations will have this, so perhaps it should go in the base. for( UINT ii=0; iiAddRef(); CPUTAssetLibraryDX11 *pAssetLibrary = (CPUTAssetLibraryDX11*)CPUTAssetLibrary::GetAssetLibrary(); for(UINT ii=0; iiGetValueByName(_L("type"))->ValueAsString(); CPUTRenderNode *pParentNode = NULL; // TODO: use Get*() instead of Load*() ? cString name = pBlock->GetValueByName(_L("name"))->ValueAsString(); int parentIndex; CPUTRenderNode *pNode = NULL; if(0==nodeType.compare(_L("null"))) { pNode = pNode = new CPUTNullNode(); result = ((CPUTNullNode*)pNode)->LoadNullNode(pBlock, &parentIndex); pParentNode = mppAssetList[parentIndex+1]; cString &parentPrefix = pParentNode->GetPrefix(); pNode->SetPrefix( parentPrefix + _L(".") + name ); pAssetLibrary->AddNullNode(parentPrefix + name, (CPUTNullNode*)pNode); // Add this null's name to our prefix // Append this null's name to our parent's prefix pNode->SetParent( pParentNode ); pParentNode->AddChild( pNode ); } else if(0==nodeType.compare(_L("model"))) { CPUTConfigEntry *pValue = pBlock->GetValueByName( _L("instance") ); CPUTModelDX11 *pModel = new CPUTModelDX11(); if( pValue == &CPUTConfigEntry::sNullConfigValue ) { // Not found. So, not an instance. pModel->LoadModel(pBlock, &parentIndex, NULL); } else { int instance = pValue->ValueAsInt(); pModel->LoadModel(pBlock, &parentIndex, (CPUTModel*)mppAssetList[instance+1]); } pParentNode = mppAssetList[parentIndex+1]; pModel->SetParent( pParentNode ); pParentNode->AddChild( pModel ); cString &parentPrefix = pParentNode->GetPrefix(); pModel->SetPrefix( parentPrefix ); pAssetLibrary->AddModel(parentPrefix + name, pModel); pModel->UpdateBoundsWorldSpace(); #ifdef SUPPORT_DRAWING_BOUNDING_BOXES // Create a mesh for rendering the bounding box // TODO: There is definitely a better way to do this. But, want to see the bounding boxes! pModel->CreateBoundingBoxMesh(); #endif pNode = pModel; } else if(0==nodeType.compare(_L("light"))) { pNode = new CPUTLight(); ((CPUTLight*)pNode)->LoadLight(pBlock, &parentIndex); pParentNode = mppAssetList[parentIndex+1]; // +1 because we added a root node to the start pNode->SetParent( pParentNode ); pParentNode->AddChild( pNode ); cString &parentPrefix = pParentNode->GetPrefix(); pNode->SetPrefix( parentPrefix ); pAssetLibrary->AddLight(parentPrefix + name, (CPUTLight*)pNode); } else if(0==nodeType.compare(_L("camera"))) { pNode = new CPUTCamera(); ((CPUTCamera*)pNode)->LoadCamera(pBlock, &parentIndex); pParentNode = mppAssetList[parentIndex+1]; // +1 because we added a root node to the start pNode->SetParent( pParentNode ); pParentNode->AddChild( pNode ); cString &parentPrefix = pParentNode->GetPrefix(); pNode->SetPrefix( parentPrefix ); pAssetLibrary->AddCamera(parentPrefix + name, (CPUTCamera*)pNode); if( !mpFirstCamera ) { mpFirstCamera = (CPUTCamera*)pNode; mpFirstCamera->AddRef();} ++mCameraCount; } else { ASSERT(0,_L("Unsupported node type '") + nodeType + _L("'.")); } // Add the node to our asset list (i.e., the linear list, not the hierarchical) mppAssetList[ii+1] = pNode; // Don't AddRef.Creating it set the refcount to 1. We add it to the list, and then we're done with it. // Net effect is 0 (+1 to add to list, and -1 because we're done with it) // pNode->AddRef(); } return result; } //----------------------------------------------------------------------------- CPUTAssetSet *CPUTAssetSetDX11::CreateAssetSet( const cString &name, const cString &absolutePathAndFilename ) { CPUTAssetLibraryDX11 *pAssetLibrary = ((CPUTAssetLibraryDX11*)CPUTAssetLibrary::GetAssetLibrary()); // Create the root node. CPUTNullNode *pRootNode = new CPUTNullNode(); pRootNode->SetName(_L("_CPUTAssetSetRootNode_")); // Create the asset set, set its root, and load it CPUTAssetSet *pNewAssetSet = new CPUTAssetSetDX11(); pNewAssetSet->SetRoot( pRootNode ); pAssetLibrary->AddNullNode( name + _L("_Root"), pRootNode ); CPUTResult result = pNewAssetSet->LoadAssetSet(absolutePathAndFilename); if( CPUTSUCCESS(result) ) { pAssetLibrary->AddAssetSet(name, pNewAssetSet); return pNewAssetSet; } ASSERT( CPUTSUCCESS(result), _L("Error loading AssetSet\n'")+absolutePathAndFilename+_L("'")); pNewAssetSet->Release(); return NULL; } ================================================ FILE: CPUT/CPUT/CPUTAssetSetDX11.h ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #ifndef __CPUTASSETSETDX11_H__ #define __CPUTASSETSETDX11_H__ #include "CPUTAssetSet.h" class CPUTAssetSetDX11 : public CPUTAssetSet { public: static CPUTAssetSet *CreateAssetSet( const cString &name, const cString &absolutePathAndFilename ); CPUTAssetSetDX11() : CPUTAssetSet() {} virtual ~CPUTAssetSetDX11(); virtual CPUTResult LoadAssetSet(cString name); }; #endif // #ifndef __CPUTASSETSETDX11_H__ ================================================ FILE: CPUT/CPUT/CPUTBuffer.cpp ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #include "CPUTBuffer.h" #ifdef CPUT_FOR_DX11 #include "CPUTBufferDX11.h" #else #error You must supply a target graphics API (ex: #define CPUT_FOR_DX11), or implement the target API for this file. #endif ================================================ FILE: CPUT/CPUT/CPUTBuffer.h ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #ifndef _CPUTBUFFER_H #define _CPUTBUFFER_H #include "CPUT.h" #include "CPUTRefCount.h" // TODO: Move to dedicated file class CPUTBuffer : public CPUTRefCount { protected: cString mName; eCPUTMapType mMappedType; ~CPUTBuffer(){ mName.clear(); } // Destructor is not public. Must release instead of delete. public: CPUTBuffer(){mMappedType = CPUT_MAP_UNDEFINED;} CPUTBuffer(cString &name) {mName = name; mMappedType = CPUT_MAP_UNDEFINED;} }; #endif //_CPUTBUFFER_H ================================================ FILE: CPUT/CPUT/CPUTBufferDX11.cpp ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #include "CPUTBufferDX11.h" //----------------------------------------------------------------------------- D3D11_MAPPED_SUBRESOURCE CPUTBufferDX11::MapBuffer( CPUTRenderParameters ¶ms, eCPUTMapType type, bool wait ) { // Mapping for DISCARD requires dynamic buffer. Create dynamic copy? // Could easily provide input flag. But, where would we specify? Don't like specifying in the .set file // Because mapping is something the application wants to do - it isn't inherent in the data. // Could do Clone() and pass dynamic flag to that. // But, then we have two. Could always delete the other. // Could support programatic flag - apply to all loaded models in the .set // Could support programatic flag on model. Load model first, then load set. // For now, simply support CopyResource mechanism. HRESULT hr; ID3D11Device *pD3dDevice = CPUT_DX11::GetDevice(); CPUTRenderParametersDX *pParamsDX11 = (CPUTRenderParametersDX*)¶ms; ID3D11DeviceContext *pContext = pParamsDX11->mpContext; if( !mpBufferStaging ) { D3D11_BUFFER_DESC desc; mpBuffer->GetDesc( &desc ); desc.Usage = D3D11_USAGE_STAGING; switch( type ) { case CPUT_MAP_READ: desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; desc.BindFlags = 0; break; case CPUT_MAP_READ_WRITE: desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE; desc.BindFlags = 0; break; case CPUT_MAP_WRITE: case CPUT_MAP_WRITE_DISCARD: case CPUT_MAP_NO_OVERWRITE: desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; desc.BindFlags = 0; break; }; hr = pD3dDevice->CreateBuffer( &desc, NULL, &mpBufferStaging ); ASSERT( SUCCEEDED(hr), _L("Failed to create staging buffer") ); CPUTSetDebugName( mpBufferStaging, _L("Staging Buffer")); } else { ASSERT( mMappedType == type, _L("Mapping with a different CPU access than creation parameter.") ); } D3D11_MAPPED_SUBRESOURCE info; switch( type ) { case CPUT_MAP_READ: case CPUT_MAP_READ_WRITE: // TODO: Copying and immediately mapping probably introduces a stall. // Expose the copy externally? // TODO: copy only if changed? // Copy only first time? // Copy the GPU version before we read from it. pContext->CopyResource( mpBufferStaging, mpBuffer ); break; }; hr = pContext->Map( mpBufferStaging, wait ? 0 : D3D11_MAP_FLAG_DO_NOT_WAIT, (D3D11_MAP)type, 0, &info ); mMappedType = type; return info; } // CPUTBufferDX11::Map() //----------------------------------------------------------------------------- void CPUTBufferDX11::UnmapBuffer( CPUTRenderParameters ¶ms ) { ASSERT( mMappedType != CPUT_MAP_UNDEFINED, _L("Can't unmap a render target that isn't mapped.") ); CPUTRenderParametersDX *pParamsDX11 = (CPUTRenderParametersDX*)¶ms; ID3D11DeviceContext *pContext = pParamsDX11->mpContext; pContext->Unmap( mpBufferStaging, 0 ); // If we were mapped for write, then copy staging buffer to GPU switch( mMappedType ) { case CPUT_MAP_READ: break; case CPUT_MAP_READ_WRITE: case CPUT_MAP_WRITE: case CPUT_MAP_WRITE_DISCARD: case CPUT_MAP_NO_OVERWRITE: pContext->CopyResource( mpBuffer, mpBufferStaging ); break; }; } // CPUTBufferDX11::Unmap() ================================================ FILE: CPUT/CPUT/CPUTBufferDX11.h ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #ifndef _CPUTBUFFERDX11_H #define _CPUTBUFFERDX11_H #include "CPUTBuffer.h" #include "CPUT_DX11.h" //-------------------------------------------------------------------------------------- // TODO: Move to dedicated file class CPUTBufferDX11 : public CPUTBuffer { private: // resource view pointer ID3D11ShaderResourceView *mpShaderResourceView; ID3D11UnorderedAccessView *mpUnorderedAccessView; ID3D11Buffer *mpBuffer; ID3D11Buffer *mpBufferStaging; // Destructor is not public. Must release instead of delete. ~CPUTBufferDX11() { SAFE_RELEASE( mpShaderResourceView ); SAFE_RELEASE( mpUnorderedAccessView ); SAFE_RELEASE( mpBuffer ); SAFE_RELEASE( mpBufferStaging ); } public: CPUTBufferDX11() : mpShaderResourceView(NULL), mpUnorderedAccessView(NULL), mpBuffer(NULL), mpBufferStaging(NULL) { } CPUTBufferDX11(cString &name, ID3D11Buffer *pBuffer) : mpBuffer(pBuffer), mpBufferStaging(NULL), mpShaderResourceView(NULL), mpUnorderedAccessView(NULL), CPUTBuffer(name) { if(pBuffer) pBuffer->AddRef(); } CPUTBufferDX11(cString &name, ID3D11Buffer *pBuffer, ID3D11ShaderResourceView *pView) : mpBuffer(pBuffer), mpBufferStaging(NULL), mpShaderResourceView(pView), mpUnorderedAccessView(NULL), CPUTBuffer(name) { if(pBuffer) pBuffer->AddRef(); if(pView) pView->AddRef(); } CPUTBufferDX11(cString &name, ID3D11Buffer *pBuffer, ID3D11UnorderedAccessView *pView) : mpBuffer(pBuffer), mpBufferStaging(NULL), mpShaderResourceView(NULL), mpUnorderedAccessView(pView), CPUTBuffer(name) { if(pBuffer) pBuffer->AddRef(); if(pView) pView->AddRef(); } ID3D11ShaderResourceView *GetShaderResourceView() { return mpShaderResourceView; } ID3D11UnorderedAccessView *GetUnorderedAccessView() { return mpUnorderedAccessView; } void SetShaderResourceView(ID3D11ShaderResourceView *pShaderResourceView) { // release any resource view we might already be pointing too SAFE_RELEASE( mpShaderResourceView ); mpShaderResourceView = pShaderResourceView; mpShaderResourceView->AddRef(); } void SetUnorderedAccessView(ID3D11UnorderedAccessView *pUnorderedAccessView) { // release any resource view we might already be pointing too SAFE_RELEASE( mpUnorderedAccessView ); mpUnorderedAccessView = pUnorderedAccessView; mpUnorderedAccessView->AddRef(); } void SetBufferAndViews(ID3D11Buffer *pBuffer, ID3D11ShaderResourceView *pShaderResourceView, ID3D11UnorderedAccessView *pUnorderedAccessView ) { SAFE_RELEASE(mpBuffer); mpBuffer = pBuffer; if(mpBuffer) mpBuffer->AddRef(); // release any resource view we might already be pointing too SAFE_RELEASE( mpShaderResourceView ); mpShaderResourceView = pShaderResourceView; if(mpShaderResourceView) mpShaderResourceView->AddRef(); // release any resource view we might already be pointing too SAFE_RELEASE( mpUnorderedAccessView ); mpUnorderedAccessView = pUnorderedAccessView; if(mpUnorderedAccessView) mpUnorderedAccessView->AddRef(); } ID3D11Buffer *GetNativeBuffer() { return mpBuffer; } D3D11_MAPPED_SUBRESOURCE MapBuffer( CPUTRenderParameters ¶ms, eCPUTMapType type, bool wait=true ); void UnmapBuffer( CPUTRenderParameters ¶ms ); void ReleaseBuffer() { SAFE_RELEASE(mpShaderResourceView); SAFE_RELEASE(mpUnorderedAccessView); SAFE_RELEASE(mpBuffer); SAFE_RELEASE(mpBufferStaging); } }; #endif //_CPUTBUFFERDX11_H ================================================ FILE: CPUT/CPUT/CPUTButton.cpp ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #include "CPUTButton.h" #include "CPUTText.h" // static initializers bool CPUTButton::mStaticRegistered = false; // list of resources sizes CPUT_SIZE CPUTButton::mpButtonIdleImageSizeList[CPUT_NUM_IMAGES_IN_BUTTON] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; CPUT_SIZE CPUTButton::mpButtonPressedImageSizeList[CPUT_NUM_IMAGES_IN_BUTTON] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; CPUT_SIZE CPUTButton::mpButtonDisabledImageSizeList[CPUT_NUM_IMAGES_IN_BUTTON] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; int CPUTButton::mSmallestLeftSizeIdle=0; int CPUTButton::mSmallestRightSizeIdle=0; int CPUTButton::mSmallestTopSizeIdle=0; int CPUTButton::mSmallestBottomSizeIdle=0; int CPUTButton::mSmallestLeftSizePressed=0; int CPUTButton::mSmallestRightSizePressed=0; int CPUTButton::mSmallestTopSizePressed=0; int CPUTButton::mSmallestBottomSizePressed=0; int CPUTButton::mSmallestLeftSizeDisabled=0; int CPUTButton::mSmallestRightSizeDisabled=0; int CPUTButton::mSmallestTopSizeDisabled=0; int CPUTButton::mSmallestBottomSizeDisabled=0; // texture atlas information float gAtlasWidth = 256.0f; float gAtlasHeight = 64.0f; // Texture atlas coordinates of the active-idle button image int gUVLocations_active[] = { 8,8, 15,31, // lt 8,31, 15,32, // lm 8,32, 15,40, // lb 15,8, 16,31, // mt 15,31, 16,32, // mm 15,32, 16,40, // mb 16,8, 25,31, // rt 16,31, 25,32, // rm 16,32, 25,40 // rb }; // Texture atlas coordinates of the pressed button image int gUVLocations_pressed[] = { 63,8, 70,31, // lt 63,31, 70,32, // lm 63,32, 70,40, // lb 70,8, 71,31, // mt 70,31, 71,32, // mm 70,32, 71,40, // mb 71,8, 81,31, // rt 71,31, 81,32, // rm 71,32, 81,40 // rb }; // Texture atlas coordinates of the disabled button image int gUVLocations_disabled[] = { 36,8, 43,31, // lt 36,31, 43,32, // lm 36,32, 43,40, // lb 43,8, 45,31, // mt 43,31, 45,32, // mm 43,32, 45,40, // mb 45,8, 55,31, // rt 45,31, 55,32, // rm 45,32, 55,40 // rb }; float3 mpUVCoords_active[9*2]; float3 mpUVCoords_pressed[9*2]; float3 mpUVCoords_disabled[9*2]; // Constructor //----------------------------------------------------------------------------- CPUTButton::CPUTButton(const cString ControlText, CPUTControlID id, CPUTFont *pFont): mbMouseInside(false), mbStartedClickInside(false), mpButtonText(NULL), mpMirrorBufferActive(NULL), mpMirrorBufferPressed(NULL), mpMirrorBufferDisabled(NULL), mpFont(pFont) { // initialize the state variables InitializeState(); // save the control ID for callbacks mcontrolID = id; // save the font to use for text on this button mpFont = pFont; // set as enabled CPUTControl::SetEnable(true); // initialize the size lists memset(&mpButtonIdleSizeList, 0, sizeof(CPUT_SIZE) * CPUT_NUM_IMAGES_IN_BUTTON); memset(&mpButtonPressedSizeList, 0, sizeof(CPUT_SIZE) * CPUT_NUM_IMAGES_IN_BUTTON); memset(&mpButtonDisabledSizeList, 0, sizeof(CPUT_SIZE) * CPUT_NUM_IMAGES_IN_BUTTON); // set up the per-instance data RegisterInstanceResources(); // set the text on the button and resize it accordingly SetText(ControlText); // set the default control position SetPosition( 0, 0 ); } // Initial state of the control's member variables //----------------------------------------------------------------------------- void CPUTButton::InitializeState() { mcontrolType = CPUT_BUTTON; mButtonState = CPUT_BUTTON_NEUTRAL; // dimensions mButtonDimensions.x=0; mButtonDimensions.y=0; mButtonDimensions.width=0; mButtonDimensions.height=0; } // Destructor //------------------------------------------------------------------------------ CPUTButton::~CPUTButton() { UnRegisterInstanceResources(); } // Return the upper-left screen coordinate location of this control //-------------------------------------------------------------------------------- void CPUTButton::GetPosition(int &x, int &y) { x = mButtonDimensions.x; y = mButtonDimensions.y; } // Return the width/height of the control //-------------------------------------------------------------------------------- void CPUTButton::GetDimensions(int &width, int &height) { width = mButtonDimensions.width; height = mButtonDimensions.height; } // Returns the number of quads needed to draw this control //-------------------------------------------------------------------------------- unsigned int CPUTButton::GetOutputVertexCount() { // A button is always made of 9 quads. // Quads 2,4,5,6, and 8 'stretch' in height and/or width to fit the // static text/content inside the button // // ---+-----------+--- // | 1 | 4 | 7 | // |---+-----------+---| // | | | | // | 2 | 5 | 8 | // | | | | // |---+-----------+---| // | 3 | 6 | 9 | // ---+-----------+--- // // calculation: 3 verts/triangle * 2 triangle/quad * 9 quads return (2*3)*9; } //CPUTEventHandler // Handle keyboard events //-------------------------------------------------------------------------------- CPUTEventHandledCode CPUTButton::HandleKeyboardEvent(CPUTKey key) { UNREFERENCED_PARAMETER(key); return CPUT_EVENT_UNHANDLED; } // Handle mouse events //-------------------------------------------------------------------------------- CPUTEventHandledCode CPUTButton::HandleMouseEvent(int x, int y, int wheel, CPUTMouseState state) { UNREFERENCED_PARAMETER(wheel); CPUTEventHandledCode handledCode = CPUT_EVENT_UNHANDLED; if((CPUT_CONTROL_INACTIVE == mControlState) || (false == mControlVisible) ) { mbMouseInside = false; return handledCode; } // if we're continuing to be pressed, move around with the mouse movement if( (CPUT_BUTTON_PRESSED == mButtonState ) && (CPUT_MOUSE_LEFT_DOWN == state)) { return CPUT_EVENT_HANDLED; } if(ContainsPoint(x,y)) { // did we start our click inside the button? if((state & CPUT_MOUSE_LEFT_DOWN) && (true == mbMouseInside)) { mbStartedClickInside = true; mButtonState = CPUT_BUTTON_PRESSED; handledCode = CPUT_EVENT_HANDLED; // tell gui system this control image is now dirty // and needs to rebuild it's draw list mControlGraphicsDirty = true; } // did they click inside the button? if(!(state & CPUT_MOUSE_LEFT_DOWN) && (true == mbStartedClickInside) && (CPUT_BUTTON_PRESSED == mButtonState)) { // they let up the click - trigger the user's callback mpCallbackHandler->HandleCallbackEvent(1, mcontrolID, (CPUTControl*) this); handledCode = CPUT_EVENT_HANDLED; mButtonState = CPUT_BUTTON_NEUTRAL; // tell gui system this control image is now dirty // and needs to rebuild it's draw list mControlGraphicsDirty = true; } if(!(state & CPUT_MOUSE_LEFT_DOWN)) { mbMouseInside = true; } } else { // we left the button // if we weren't already in neutral state, return to neutral sate // this handles case of clicking button, exiting button, and releasing button outside control if(CPUT_BUTTON_NEUTRAL != mButtonState) { mButtonState = CPUT_BUTTON_NEUTRAL; mControlGraphicsDirty = true; } mbMouseInside = false; mButtonState = CPUT_BUTTON_NEUTRAL; mbStartedClickInside = false; } return handledCode; } // Returns true if the x,y coordinate is inside the button's control region //-------------------------------------------------------------------------------- bool CPUTButton::ContainsPoint(int x, int y) { if( (x>=mButtonDimensions.x) && (x<=mButtonDimensions.x+mButtonDimensions.width)) { if( (y>=mButtonDimensions.y) && (y<=mButtonDimensions.y+mButtonDimensions.height)) { return true; } } return false; } // Returns the x,y coordinate inside the button area that should be 'safe' to draw on //-------------------------------------------------------------------------------- void CPUTButton::GetInsetTextCoordinate(int &x, int &y) { // get text size CPUT_RECT ButtonTextDimensions; if(mpButtonText) { mpButtonText->GetDimensions(ButtonTextDimensions.width, ButtonTextDimensions.height); } else { ButtonTextDimensions.width=0; ButtonTextDimensions.height=0; } // calculate a good 'center' point x =(int) ( mButtonDimensions.x + mButtonDimensions.width/2.0f - ButtonTextDimensions.width/2.0f); y =(int) ( mButtonDimensions.y + mButtonDimensions.height/2.0f - ButtonTextDimensions.height/2.0f); } // Sets the text on the control //-------------------------------------------------------------------------------- void CPUTButton::SetText(const cString String) { // Zero out the size and location InitializeState(); // create the static text object if it doesn't exist if(NULL == mpButtonText) { mpButtonText = new CPUTText(mpFont); } // set the Static control's text mpButtonText->SetText(String); // get the dimensions of the string in pixels CPUT_RECT rect; mpButtonText->GetDimensions(rect.width, rect.height); // resize this control to fix that string with padding Resize(rect.width, rect.height); // move the text to a nice inset location inside the 'safe' area // of the button image int x,y; GetInsetTextCoordinate(x, y); mpButtonText->SetPosition(x, y); // position or size may move - force a recalculation of this control's location // if it is managed by the auto-arrange function if(this->IsAutoArranged()) { mControlNeedsArrangmentResizing = true; } else { // otherwise, we mark this as dirty mControlGraphicsDirty = true; } } // sets the dimensions of the button //-------------------------------------------------------------------------------- void CPUTButton::SetDimensions(int width, int height) { // Zero out the size and location InitializeState(); // get the dimensions of the string in pixels CPUT_RECT rect; mpButtonText->GetDimensions(rect.width, rect.height); width = max(rect.width, width); height = max(rect.height, height); // resize this control to fix that string with padding Resize(width, height); // move the text to a nice inset location inside the 'safe' area // of the button image int x,y; GetInsetTextCoordinate(x, y); mpButtonText->SetPosition(x, y); } // Fills the users buffer with the button text //-------------------------------------------------------------------------------- void CPUTButton::GetText(cString &String) { if(mpButtonText) { mpButtonText->GetString(String); } } // Enable/disable the control //-------------------------------------------------------------------------------- void CPUTButton::SetEnable(bool in_bEnabled) { // chain to CPUTControl set enabled CPUTControl::SetEnable(in_bEnabled); // set the control's text to match mpButtonText->SetEnable(in_bEnabled); // otherwise, we mark this as dirty mControlGraphicsDirty = true; } // Set the upper-left screen coordinate location of this control //-------------------------------------------------------------------------------- void CPUTButton::SetPosition(int x, int y) { // move the button graphics mButtonDimensions.x = x; mButtonDimensions.y = y; // move the static text (if any) if(mpButtonText) { // resize things in the buffers CPUT_RECT rect; mpButtonText->GetDimensions(rect.width, rect.height); Resize(rect.width, rect.height); int insetX, insetY; GetInsetTextCoordinate(insetX, insetY); mpButtonText->SetPosition(insetX, insetY); } } // 'Draw' this control into the supplied vertex buffer object //-------------------------------------------------------------------------------- void CPUTButton::DrawIntoBuffer(CPUTGUIVertex *pVertexBufferMirror, UINT *pInsertIndex, UINT pMaxBufferSize, CPUTGUIVertex *pTextVertexBufferMirror, UINT *pTextInsertIndex, UINT MaxTextVertexBufferSize) { if(!mControlVisible) { return; } // invalid output buffer pointers? if((NULL==pVertexBufferMirror) || (NULL==pInsertIndex)) { return; } // invalid buffer pointers? if(!mpMirrorBufferActive || !mpMirrorBufferPressed || !mpMirrorBufferDisabled) { return; } // Do we have enough room to put this control into the output buffer? int VertexCopyCount = GetOutputVertexCount(); ASSERT( (pMaxBufferSize >= *pInsertIndex + VertexCopyCount), _L("Too many CPUT GUI controls for allocated GUI buffer. Allocated GUI vertex buffer is too small.\n\nIncrease CPUT_GUI_BUFFER_SIZE size.") ); switch(mControlState) { case CPUT_CONTROL_ACTIVE: // copy the active+idle button into the stream if(CPUT_BUTTON_NEUTRAL == mButtonState) { memcpy(&pVertexBufferMirror[*pInsertIndex], mpMirrorBufferActive, sizeof(CPUTGUIVertex)*VertexCopyCount); } // copy the pressed button into the stream if(CPUT_BUTTON_PRESSED == mButtonState) { memcpy(&pVertexBufferMirror[*pInsertIndex], mpMirrorBufferPressed, sizeof(CPUTGUIVertex)*VertexCopyCount); } break; case CPUT_CONTROL_INACTIVE: // copy the inactive button into the stream memcpy(&pVertexBufferMirror[*pInsertIndex], mpMirrorBufferDisabled, sizeof(CPUTGUIVertex)*VertexCopyCount); break; default: // error! unknown state ASSERT(0,_L("CPUTButton: Control is in unknown state")); return; } // move the index the correct number of floats to account // for 9 new quads, each quad with 6 verts in it (and each vert with 3+2 floats in it). *pInsertIndex+= VertexCopyCount; // now do the text // draw the text if(mpButtonText) { mpButtonText->DrawIntoBuffer(pTextVertexBufferMirror, pTextInsertIndex, MaxTextVertexBufferSize); } // we'll mark the control as no longer being 'dirty' mControlGraphicsDirty = false; } // Allocates/registers resources used by all buttons //-------------------------------------------------------------------------------- CPUTResult CPUTButton::RegisterStaticResources() { // calculate the UV coordinates of each of the 9 images that // make up a button. Do this for the active, pressed, and disabled states. for(int ii=0; ii<18; ii++) { mpUVCoords_active[ii].x = gUVLocations_active[2*ii]/gAtlasWidth; mpUVCoords_active[ii].y = gUVLocations_active[2*ii+1]/gAtlasHeight; mpUVCoords_pressed[ii].x = gUVLocations_pressed[2*ii]/gAtlasWidth; mpUVCoords_pressed[ii].y = gUVLocations_pressed[2*ii+1]/gAtlasHeight; mpUVCoords_disabled[ii].x = gUVLocations_disabled[2*ii]/gAtlasWidth; mpUVCoords_disabled[ii].y = gUVLocations_disabled[2*ii+1]/gAtlasHeight; } // calculate the width/height in pixels of each of the 9 image slices // that makes up the button images int QuadIndex=0; for(int ii=0; ii<9*4; ii+=4) { mpButtonIdleImageSizeList[QuadIndex].width = gUVLocations_active[ii+2] - gUVLocations_active[ii+0]; mpButtonIdleImageSizeList[QuadIndex].height = gUVLocations_active[ii+3] - gUVLocations_active[ii+1]; mpButtonPressedImageSizeList[QuadIndex].width = gUVLocations_pressed[ii+2] - gUVLocations_pressed[ii+0]; mpButtonPressedImageSizeList[QuadIndex].height = gUVLocations_pressed[ii+3] - gUVLocations_pressed[ii+1]; mpButtonDisabledImageSizeList[QuadIndex].width = gUVLocations_disabled[ii+2] - gUVLocations_disabled[ii+0]; mpButtonDisabledImageSizeList[QuadIndex].height = gUVLocations_disabled[ii+3] - gUVLocations_disabled[ii+1]; QuadIndex++; } // find the narrowest (width) left side images mSmallestLeftSizeIdle = min(min(mpButtonIdleImageSizeList[0].width, mpButtonIdleImageSizeList[1].width), mpButtonIdleImageSizeList[2].width); mSmallestLeftSizePressed = min(min(mpButtonPressedImageSizeList[0].width, mpButtonPressedImageSizeList[1].width), mpButtonPressedImageSizeList[2].width); mSmallestLeftSizeDisabled = min(min(mpButtonDisabledImageSizeList[0].width, mpButtonDisabledImageSizeList[1].width), mpButtonDisabledImageSizeList[2].width); // find the narrowest (width) right side images mSmallestRightSizeIdle = min(min(mpButtonIdleImageSizeList[6].width, mpButtonIdleImageSizeList[7].width), mpButtonIdleImageSizeList[8].width); mSmallestRightSizePressed = min(min(mpButtonPressedImageSizeList[6].width, mpButtonPressedImageSizeList[7].width), mpButtonPressedImageSizeList[8].width); mSmallestRightSizeDisabled = min(min(mpButtonDisabledImageSizeList[6].width, mpButtonDisabledImageSizeList[7].width), mpButtonDisabledImageSizeList[8].width); // find the shortest (height) of the top row of images mSmallestTopSizeIdle = min(min( mpButtonIdleImageSizeList[0].height,mpButtonIdleImageSizeList[3].height), mpButtonIdleImageSizeList[6].height); mSmallestTopSizePressed = min(min( mpButtonPressedImageSizeList[0].height,mpButtonPressedImageSizeList[3].height), mpButtonPressedImageSizeList[6].height); mSmallestTopSizeDisabled = min( min( mpButtonDisabledImageSizeList[0].height,mpButtonDisabledImageSizeList[3].height), mpButtonDisabledImageSizeList[6].height); // find the shortest (height) of the bottom row of images mSmallestBottomSizeIdle = min(min( mpButtonIdleImageSizeList[2].height,mpButtonIdleImageSizeList[5].height), mpButtonIdleImageSizeList[8].height); mSmallestBottomSizePressed = min(min( mpButtonPressedImageSizeList[2].height,mpButtonPressedImageSizeList[5].height), mpButtonPressedImageSizeList[8].height); mSmallestBottomSizeDisabled = min(min( mpButtonDisabledImageSizeList[2].height,mpButtonDisabledImageSizeList[5].height), mpButtonDisabledImageSizeList[8].height); mStaticRegistered = true; return CPUT_SUCCESS; } // Deletes any statically allocated resources used for all buttons //-------------------------------------------------------------------------------- CPUTResult CPUTButton::UnRegisterStaticResources() { return CPUT_SUCCESS; } // Allocates an initialize all per-instance resources for this button //-------------------------------------------------------------------------------- CPUTResult CPUTButton::RegisterInstanceResources() { // clear any previously allocated buffers SAFE_DELETE_ARRAY(mpMirrorBufferActive); SAFE_DELETE_ARRAY(mpMirrorBufferPressed); SAFE_DELETE_ARRAY(mpMirrorBufferDisabled); // allocate the per-instance sizes (each button will have different dimensions) mpMirrorBufferActive = new CPUTGUIVertex[6 * 9]; mpMirrorBufferPressed = new CPUTGUIVertex[6 * 9]; mpMirrorBufferDisabled = new CPUTGUIVertex[6 * 9]; // store all the default button component quad sizes in instance variables // Re-sizable parts will get re-calculated during setText and other operations for(int i=0; i width) { width = safeWidth; } if(safeHeight > height) { height = safeHeight; } // add some padding for nicety width += CPUT_BUTTON_TEXT_BORDER_PADDING_X; height += CPUT_BUTTON_TEXT_BORDER_PADDING_Y; { // store the new dimensions mButtonDimensions.width = width; mButtonDimensions.height = height; // calculate the pieces we'll need to rebuild int middleWidth = width - mSmallestLeftSizeIdle - mSmallestRightSizeIdle; int middleHeight = height - mSmallestTopSizeIdle - mSmallestBottomSizeIdle; // delete the old button quads for the middle sections //result = UnRegisterResizableInstanceQuads(); // create a new quads with the correct size // Idle button quads // left AddQuadIntoMirrorBuffer(mpMirrorBufferActive, 0*6, (float)mButtonDimensions.x, (float)mButtonDimensions.y, (float) mpButtonIdleSizeList[0].width, (float) mpButtonIdleSizeList[0].height, mpUVCoords_active[2*0], mpUVCoords_active[2*0+1] ); AddQuadIntoMirrorBuffer(mpMirrorBufferActive, 1*6, (float)mButtonDimensions.x, (float)mButtonDimensions.y+mSmallestTopSizeIdle, (float) mpButtonIdleSizeList[1].width, (float) middleHeight, mpUVCoords_active[2*1], mpUVCoords_active[2*1+1] ); mpButtonIdleSizeList[1].height = middleHeight; AddQuadIntoMirrorBuffer(mpMirrorBufferActive, 2*6, (float)mButtonDimensions.x, (float)mButtonDimensions.y+mSmallestTopSizeIdle+middleHeight, (float) mpButtonIdleSizeList[2].width, (float) mpButtonIdleSizeList[2].height, mpUVCoords_active[2*2], mpUVCoords_active[2*2+1] ); // middle AddQuadIntoMirrorBuffer(mpMirrorBufferActive, 3*6, (float)mButtonDimensions.x+mSmallestLeftSizeIdle, (float)mButtonDimensions.y, (float) middleWidth, (float) mpButtonIdleSizeList[3].height, mpUVCoords_active[2*3], mpUVCoords_active[2*3+1] ); mpButtonIdleSizeList[3].width = middleWidth; AddQuadIntoMirrorBuffer(mpMirrorBufferActive, 4*6, (float)mButtonDimensions.x+mSmallestLeftSizeIdle, (float)mButtonDimensions.y+mSmallestTopSizeIdle, (float) middleWidth, (float) middleHeight, mpUVCoords_active[2*4], mpUVCoords_active[2*4+1] ); mpButtonIdleSizeList[4].width = middleWidth; mpButtonIdleSizeList[4].height = middleHeight; AddQuadIntoMirrorBuffer(mpMirrorBufferActive, 5*6, (float)mButtonDimensions.x+mSmallestLeftSizeIdle, (float)mButtonDimensions.y+mSmallestTopSizeIdle+middleHeight, (float) middleWidth, (float) mpButtonIdleSizeList[5].height, mpUVCoords_active[2*5], mpUVCoords_active[2*5+1] ); mpButtonIdleSizeList[5].width = middleWidth; // right AddQuadIntoMirrorBuffer(mpMirrorBufferActive, 6*6, (float)mButtonDimensions.x+mSmallestLeftSizeIdle+middleWidth, (float)mButtonDimensions.y, (float) mpButtonIdleSizeList[6].width, (float)mpButtonIdleSizeList[6].height, mpUVCoords_active[2*6], mpUVCoords_active[2*6+1] ); AddQuadIntoMirrorBuffer(mpMirrorBufferActive, 7*6, (float)mButtonDimensions.x+mSmallestLeftSizeIdle+middleWidth, (float)mButtonDimensions.y+mSmallestTopSizeIdle, (float) mpButtonIdleSizeList[7].width, (float)middleHeight, mpUVCoords_active[2*7], mpUVCoords_active[2*7+1] ); mpButtonIdleSizeList[7].height = middleHeight; AddQuadIntoMirrorBuffer(mpMirrorBufferActive, 8*6, (float)mButtonDimensions.x+mSmallestLeftSizeIdle+middleWidth, (float)mButtonDimensions.y+mSmallestTopSizeIdle+middleHeight, (float) mpButtonIdleSizeList[8].width, (float)mpButtonIdleSizeList[8].height, mpUVCoords_active[2*8], mpUVCoords_active[2*8+1] ); // register uberbuffer //RegisterUberBuffer(pImmediateContext, &mpUberBufferActive, mpMirrorBufferActive); // Pressed button quads // left AddQuadIntoMirrorBuffer(mpMirrorBufferPressed, 0*6, (float)mButtonDimensions.x, (float)mButtonDimensions.y, (float) mpButtonPressedSizeList[0].width, (float) mpButtonPressedSizeList[0].height, mpUVCoords_pressed[2*0], mpUVCoords_pressed[2*0+1] ); AddQuadIntoMirrorBuffer(mpMirrorBufferPressed, 1*6, (float)mButtonDimensions.x, (float)mButtonDimensions.y+mSmallestTopSizePressed, (float) mpButtonPressedSizeList[1].width, (float) middleHeight, mpUVCoords_pressed[2*1], mpUVCoords_pressed[2*1+1] ); mpButtonPressedSizeList[1].height = middleHeight; AddQuadIntoMirrorBuffer(mpMirrorBufferPressed, 2*6, (float)mButtonDimensions.x, (float)mButtonDimensions.y+mSmallestTopSizePressed+middleHeight, (float) mpButtonPressedSizeList[2].width, (float) mpButtonPressedSizeList[2].height, mpUVCoords_pressed[2*2], mpUVCoords_pressed[2*2+1] ); // middle AddQuadIntoMirrorBuffer(mpMirrorBufferPressed, 3*6, (float)mButtonDimensions.x+mSmallestLeftSizePressed, (float)mButtonDimensions.y, (float) middleWidth, (float) mpButtonPressedSizeList[3].height, mpUVCoords_pressed[2*3], mpUVCoords_pressed[2*3+1] ); mpButtonPressedSizeList[3].width = middleWidth; AddQuadIntoMirrorBuffer(mpMirrorBufferPressed, 4*6, (float)mButtonDimensions.x+mSmallestLeftSizePressed, (float)mButtonDimensions.y+mSmallestTopSizePressed, (float) middleWidth, (float) middleHeight, mpUVCoords_pressed[2*4], mpUVCoords_pressed[2*4+1] ); mpButtonPressedSizeList[4].width = middleWidth; mpButtonPressedSizeList[4].height = middleHeight; AddQuadIntoMirrorBuffer(mpMirrorBufferPressed, 5*6, (float)mButtonDimensions.x+mSmallestLeftSizePressed, (float)mButtonDimensions.y+mSmallestTopSizePressed+middleHeight, (float) middleWidth, (float) mpButtonPressedSizeList[5].height, mpUVCoords_pressed[2*5], mpUVCoords_pressed[2*5+1] ); mpButtonPressedSizeList[5].width = middleWidth; // right AddQuadIntoMirrorBuffer(mpMirrorBufferPressed, 6*6, (float)mButtonDimensions.x+mSmallestLeftSizePressed+middleWidth, (float)mButtonDimensions.y, (float) mpButtonPressedSizeList[6].width, (float) mpButtonPressedSizeList[6].height, mpUVCoords_pressed[2*6], mpUVCoords_pressed[2*6+1] ); AddQuadIntoMirrorBuffer(mpMirrorBufferPressed, 7*6, (float)mButtonDimensions.x+mSmallestLeftSizePressed+middleWidth, (float)mButtonDimensions.y+mSmallestTopSizePressed, (float) mpButtonPressedSizeList[7].width, (float) middleHeight, mpUVCoords_pressed[2*7], mpUVCoords_pressed[2*7+1] ); mpButtonPressedSizeList[7].height = middleHeight; AddQuadIntoMirrorBuffer(mpMirrorBufferPressed, 8*6, (float)mButtonDimensions.x+mSmallestLeftSizePressed+middleWidth, (float)mButtonDimensions.y+mSmallestTopSizePressed+middleHeight, (float) mpButtonPressedSizeList[8].width, (float) mpButtonPressedSizeList[8].height, mpUVCoords_pressed[2*8], mpUVCoords_pressed[2*8+1] ); // Disabled button quads // left AddQuadIntoMirrorBuffer(mpMirrorBufferDisabled, 0*6, (float)mButtonDimensions.x, (float)mButtonDimensions.y, (float) mpButtonDisabledSizeList[0].width, (float) mpButtonDisabledSizeList[0].height, mpUVCoords_disabled[2*0], mpUVCoords_disabled[2*0+1] ); AddQuadIntoMirrorBuffer(mpMirrorBufferDisabled, 1*6, (float)mButtonDimensions.x, (float)mButtonDimensions.y+mSmallestTopSizePressed, (float) mpButtonDisabledSizeList[1].width, (float) middleHeight, mpUVCoords_disabled[2*1], mpUVCoords_disabled[2*1+1] ); mpButtonDisabledSizeList[1].height = middleHeight; AddQuadIntoMirrorBuffer(mpMirrorBufferDisabled, 2*6, (float)mButtonDimensions.x, (float)mButtonDimensions.y+mSmallestTopSizePressed+middleHeight, (float) mpButtonDisabledSizeList[2].width, (float) mpButtonDisabledSizeList[2].height, mpUVCoords_disabled[2*2], mpUVCoords_disabled[2*2+1] ); // middle AddQuadIntoMirrorBuffer(mpMirrorBufferDisabled, 3*6, (float)mButtonDimensions.x+mSmallestLeftSizePressed, (float)mButtonDimensions.y, (float) middleWidth, (float) mpButtonDisabledSizeList[3].height, mpUVCoords_disabled[2*3], mpUVCoords_disabled[2*3+1] ); mpButtonDisabledSizeList[3].width = middleWidth; AddQuadIntoMirrorBuffer(mpMirrorBufferDisabled, 4*6, (float)mButtonDimensions.x+mSmallestLeftSizePressed, (float)mButtonDimensions.y+mSmallestTopSizePressed, (float) middleWidth, (float) middleHeight, mpUVCoords_disabled[2*4], mpUVCoords_disabled[2*4+1] ); mpButtonDisabledSizeList[4].width = middleWidth; mpButtonDisabledSizeList[4].height = middleHeight; AddQuadIntoMirrorBuffer(mpMirrorBufferDisabled, 5*6, (float)mButtonDimensions.x+mSmallestLeftSizePressed, (float)mButtonDimensions.y+mSmallestTopSizePressed+middleHeight, (float) middleWidth, (float) mpButtonDisabledSizeList[5].height, mpUVCoords_disabled[2*5], mpUVCoords_disabled[2*5+1] ); mpButtonDisabledSizeList[5].width = middleWidth; // right AddQuadIntoMirrorBuffer(mpMirrorBufferDisabled, 6*6, (float)mButtonDimensions.x+mSmallestLeftSizePressed+middleWidth, (float)mButtonDimensions.y, (float) mpButtonDisabledSizeList[6].width, (float) mpButtonDisabledSizeList[6].height, mpUVCoords_disabled[2*6], mpUVCoords_disabled[2*6+1] ); AddQuadIntoMirrorBuffer(mpMirrorBufferDisabled, 7*6, (float)mButtonDimensions.x+mSmallestLeftSizePressed+middleWidth, (float)mButtonDimensions.y+mSmallestTopSizePressed, (float) mpButtonDisabledSizeList[7].width, (float) middleHeight, mpUVCoords_disabled[2*7], mpUVCoords_disabled[2*7+1] ); mpButtonDisabledSizeList[7].height = middleHeight; AddQuadIntoMirrorBuffer(mpMirrorBufferDisabled, 8*6, (float)mButtonDimensions.x+mSmallestLeftSizePressed+middleWidth, (float)mButtonDimensions.y+mSmallestTopSizePressed+middleHeight, (float) mpButtonDisabledSizeList[8].width, (float) mpButtonDisabledSizeList[8].height, mpUVCoords_disabled[2*8], mpUVCoords_disabled[2*8+1] ); // Mark this control as 'dirty' for drawing and inform the gui system that // it needs to re-calculate it's drawing buffer mControlGraphicsDirty = true; } return CPUT_SUCCESS; } // This generates a quad with the supplied coordinates/uv's/etc. //------------------------------------------------------------------------ void CPUTButton::AddQuadIntoMirrorBuffer(CPUTGUIVertex *pMirrorBuffer, int index, float x, float y, float w, float h, float3 uv1, float3 uv2 ) { CPUTColor4 color; color.r = 1.0f;color.g = 1.0f;color.b = 1.0f;color.a = 1.0f; pMirrorBuffer[index+0].Pos = float3( x + 0.0f, y + 0.0f, 1.0f); pMirrorBuffer[index+0].UV = float2(uv1.x, uv1.y); pMirrorBuffer[index+0].Color = color; pMirrorBuffer[index+1].Pos = float3( x + w, y + 0.0f, 1.0f); pMirrorBuffer[index+1].UV = float2(uv2.x, uv1.y); pMirrorBuffer[index+1].Color = color; pMirrorBuffer[index+2].Pos = float3( x + 0.0f, y + h, 1.0f); pMirrorBuffer[index+2].UV = float2(uv1.x, uv2.y); pMirrorBuffer[index+2].Color = color; pMirrorBuffer[index+3].Pos = float3( x + w, y + 0.0f, 1.0f); pMirrorBuffer[index+3].UV = float2(uv2.x, uv1.y); pMirrorBuffer[index+3].Color = color; pMirrorBuffer[index+4].Pos = float3( x + w, y + h, 1.0f); pMirrorBuffer[index+4].UV = float2(uv2.x, uv2.y); pMirrorBuffer[index+4].Color = color; pMirrorBuffer[index+5].Pos = float3( x + 0.0f, y +h, 1.0f); pMirrorBuffer[index+5].UV = float2(uv1.x, uv2.y); pMirrorBuffer[index+5].Color = color; } ================================================ FILE: CPUT/CPUT/CPUTButton.h ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #ifndef __CPUTBUTTON_H__ #define __CPUTBUTTON_H__ #include "CPUTControl.h" #include #include "CPUTGuiController.h" // forward declarations class CPUTFont; class CPUTText; // default padding between controls #define CPUT_BUTTON_TEXT_BORDER_PADDING_X 15 #define CPUT_BUTTON_TEXT_BORDER_PADDING_Y 5 typedef enum CPUTButtonState { CPUT_BUTTON_NEUTRAL, CPUT_BUTTON_PRESSED, } CPUTButtonState; const int CPUT_NUM_IMAGES_IN_BUTTON=9; const int CPUT_NUM_VERTS_IN_BUTTON_QUAD=6; // Button base - common functionality for the control //----------------------------------------------------------------------------- class CPUTButton:public CPUTControl { public: // constructors CPUTButton(CPUTButton& copy); // don't allow copy construction CPUTButton(const cString ControlText, CPUTControlID id, CPUTFont *pFont); virtual ~CPUTButton(); // CPUTControl virtual void GetPosition(int &x, int &y); virtual void GetDimensions(int &width, int &height); virtual bool ContainsPoint(int x, int y); virtual void SetPosition(int x, int y); virtual void SetText(const cString String); virtual void GetText(cString &String); virtual unsigned int GetOutputVertexCount(); virtual void SetEnable(bool in_bEnabled); //CPUTEventHandler virtual CPUTEventHandledCode HandleKeyboardEvent(CPUTKey key); virtual CPUTEventHandledCode HandleMouseEvent(int x, int y, int wheel, CPUTMouseState state); // Register assets static CPUTResult RegisterStaticResources(); static CPUTResult UnRegisterStaticResources(); CPUTResult RegisterInstanceResources(); CPUTResult UnRegisterInstanceResources(); // draw virtual void DrawIntoBuffer(CPUTGUIVertex *pVertexBufferMirror, UINT *pInsertIndex, UINT pMaxBufferSize, CPUTGUIVertex *pTextVertexBufferMirror, UINT *pTextInsertIndex, UINT MaxTextVertexBufferSize); protected: CPUT_RECT mButtonDimensions; CPUTButtonState mButtonState; // helper functions // control state bool mbMouseInside; bool mbStartedClickInside; CPUTFont *mpFont; // Static resources static bool mStaticRegistered; // sizes of unmodified button graphics static CPUT_SIZE mpButtonIdleImageSizeList[CPUT_NUM_IMAGES_IN_BUTTON]; static CPUT_SIZE mpButtonPressedImageSizeList[CPUT_NUM_IMAGES_IN_BUTTON]; static CPUT_SIZE mpButtonDisabledImageSizeList[CPUT_NUM_IMAGES_IN_BUTTON]; static int mSmallestLeftSizeIdle; static int mSmallestRightSizeIdle; static int mSmallestTopSizeIdle; static int mSmallestBottomSizeIdle; static int mSmallestLeftSizePressed; static int mSmallestRightSizePressed; static int mSmallestTopSizePressed; static int mSmallestBottomSizePressed; static int mSmallestLeftSizeDisabled; static int mSmallestRightSizeDisabled; static int mSmallestTopSizeDisabled; static int mSmallestBottomSizeDisabled; // per-instance information CPUTText *mpButtonText; CPUT_SIZE mpButtonIdleSizeList[CPUT_NUM_IMAGES_IN_BUTTON]; CPUT_SIZE mpButtonPressedSizeList[CPUT_NUM_IMAGES_IN_BUTTON]; CPUT_SIZE mpButtonDisabledSizeList[CPUT_NUM_IMAGES_IN_BUTTON]; CPUTGUIVertex *mpMirrorBufferActive; CPUTGUIVertex *mpMirrorBufferPressed; CPUTGUIVertex *mpMirrorBufferDisabled; // helper functions void InitializeState(); void SetDimensions(int width, int height); CPUTResult Resize(int width, int height); void AddQuadIntoMirrorBuffer(CPUTGUIVertex *pMirrorBuffer, int index, float x, float y, float w, float h, float3 uv1, float3 uv2 ); void GetInsetTextCoordinate(int &x, int &y); }; #endif //#ifndef __CPUTBUTTON_H__ ================================================ FILE: CPUT/CPUT/CPUTCallbackHandler.h ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #ifndef __CPUTCALLBACKHANDLER_H__ #define __CPUTCALLBACKHANDLER_H__ #include "CPUT.h" #include "CPUTControl.h" //forward declarations class CPUTControl; typedef unsigned int UINT; typedef UINT CPUTControlID; typedef UINT CPUTEventID; #define UNREFERENCED_PARAMETER(P) (P) class CPUTCallbackHandler { public: virtual void HandleCallbackEvent( CPUTEventID Event, CPUTControlID ControlID, CPUTControl *pControl ) {UNREFERENCED_PARAMETER(Event);UNREFERENCED_PARAMETER(ControlID);UNREFERENCED_PARAMETER(pControl);} }; #endif // #ifndef __CPUTCALLBACKHANDLER_H__ ================================================ FILE: CPUT/CPUT/CPUTCamera.cpp ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #include "CPUTCamera.h" #include "CPUTFrustum.h" // Constructor //----------------------------------------------------------------------------- CPUTCamera::CPUTCamera() : mFov(45.0f * 3.14159265f/180.0f), mNearPlaneDistance(1.0f), mFarPlaneDistance(100.0f), mAspectRatio(16.0f/9.0f) { // default maya position (roughly) SetPosition( 1.0f, 0.8f, 1.0f ); } // Load //----------------------------------------------------------------------------- CPUTResult CPUTCamera::LoadCamera(CPUTConfigBlock *pBlock, int *pParentID) { // TODO: Have render node load common properties. CPUTResult result = CPUT_SUCCESS; mName = pBlock->GetValueByName(_L("name"))->ValueAsString(); *pParentID = pBlock->GetValueByName(_L("parent"))->ValueAsInt(); mFov = pBlock->GetValueByName(_L("FieldOfView"))->ValueAsFloat(); mFov *= (3.14159265f/180.0f); mNearPlaneDistance = pBlock->GetValueByName(_L("NearPlane"))->ValueAsFloat(); mFarPlaneDistance = pBlock->GetValueByName(_L("FarPlane"))->ValueAsFloat(); LoadParentMatrixFromParameterBlock( pBlock ); return result; } //----------------------------------------------------------------------------- void CPUTCamera::SetAspectRatio(const float aspectRatio) { mAspectRatio = aspectRatio; } //----------------------------------------------------------------------------- void CPUTCamera::LookAt( float xx, float yy, float zz ) { float3 pos; GetPosition( &pos); float3 lookPoint(xx, yy, zz); float3 look = (lookPoint - pos).normalize(); float3 right = cross3(float3(0.0f,1.0f,0.0f), look).normalize(); // TODO: simplicy algebraically float3 up = cross3(look, right); mParentMatrix = float4x4( right.x, right.y, right.z, 0.0f, up.x, up.y, up.z, 0.0f, look.x, look.y, look.z, 0.0f, pos.x, pos.y, pos.z, 1.0f ); } //----------------------------------------------------------------------------- void CPUTCamera::SetFov(const float fov) { mFov = fov; } #define KEY_DOWN(vk) ((GetAsyncKeyState(vk) & 0x8000)?1:0) //----------------------------------------------------------------------------- void CPUTCameraControllerFPS::Update(float deltaSeconds) { float speed = mfMoveSpeed * deltaSeconds; speed *= KEY_DOWN( VK_LSHIFT ) ? 10.0f : KEY_DOWN( VK_LCONTROL) ? 0.1f : 1.0f; float4x4 *pParentMatrix = mpCamera->GetParentMatrix(); float3 vRight(pParentMatrix->getXAxis()); float3 vUp(pParentMatrix->getYAxis()); float3 vLook(pParentMatrix->getZAxis()); float3 vPositionDelta(0.0f); if(CPUTOSServices::GetOSServices()->DoesWindowHaveFocus()) { if(KEY_DOWN('W')) { vPositionDelta += vLook * speed;} if(KEY_DOWN('A')) { vPositionDelta += vRight * -speed;} if(KEY_DOWN('S')) { vPositionDelta += vLook * -speed;} if(KEY_DOWN('D')) { vPositionDelta += vRight * speed;} if(KEY_DOWN('E')) { vPositionDelta += vUp * speed;} if(KEY_DOWN('Q')) { vPositionDelta += vUp * -speed;} } float x,y,z; mpCamera->GetPosition( &x, &y, &z ); mpCamera->SetPosition( x+vPositionDelta.x, y+vPositionDelta.y, z+vPositionDelta.z ); mpCamera->Update(); } //----------------------------------------------------------------------------- CPUTEventHandledCode CPUTCameraControllerFPS::HandleMouseEvent( int x, int y, int wheel, CPUTMouseState state ) { if(state & CPUT_MOUSE_LEFT_DOWN) { float3 position = mpCamera->GetPosition(); if(!(mPrevFrameState & CPUT_MOUSE_LEFT_DOWN)) // Mouse was just clicked { mnPrevFrameX = x; mnPrevFrameY = y; } float nDeltaX = (float)(x-mnPrevFrameX); float nDeltaY = (float)(y-mnPrevFrameY); float4x4 rotationX = float4x4RotationX(nDeltaY*mfLookSpeed); float4x4 rotationY = float4x4RotationY(nDeltaX*mfLookSpeed); mpCamera->SetPosition(0.0f, 0.0f, 0.0f); // Rotate about camera center float4x4 parent = *mpCamera->GetParentMatrix(); float4x4 orientation = rotationX *parent * rotationY; orientation.orthonormalize(); mpCamera->SetParentMatrix( orientation ); mpCamera->SetPosition( position.x, position.y, position.z ); // Move back to original position mpCamera->Update(); mnPrevFrameX = x; mnPrevFrameY = y; mPrevFrameState = state; return CPUT_EVENT_HANDLED; } else { mPrevFrameState = state; return CPUT_EVENT_UNHANDLED; } } //----------------------------------------------------------------------------- CPUTEventHandledCode CPUTCameraControllerArcBall::HandleMouseEvent( int x, int y, int wheel, CPUTMouseState state ) { // TODO: We want move-in-x to orbit light in view space, not object space. if(state & CPUT_MOUSE_RIGHT_DOWN) // TODO: How to make this flexible? Want to choose which mouse button has effect. { float4x4 rotationX, rotationY; if(!(mPrevFrameState & CPUT_MOUSE_RIGHT_DOWN)) // Mouse was just clicked { mnPrevFrameX = x; mnPrevFrameY = y; } int nDeltaX = x-mnPrevFrameX; int nDeltaY = y-mnPrevFrameY; rotationY = float4x4RotationX(nDeltaY*mfLookSpeed); rotationX = float4x4RotationY(nDeltaX*mfLookSpeed); float4x4 orientation = *mpCamera->GetParentMatrix() * rotationY * rotationX; orientation.orthonormalize(); mpCamera->SetParentMatrix( orientation ); mnPrevFrameX = x; mnPrevFrameY = y; mPrevFrameState = state; return CPUT_EVENT_HANDLED; } else { mPrevFrameState = state; return CPUT_EVENT_UNHANDLED; } } ================================================ FILE: CPUT/CPUT/CPUTCamera.h ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #ifndef __CPUTCamera_H__ #define __CPUTCamera_H__ #include #include #include "CPUT.h" #include "CPUTRenderNode.h" #include "CPUTConfigBlock.h" #include "CPUTFrustum.h" //----------------------------------------------------------------------------- class CPUTCamera:public CPUTRenderNode { protected: float mFov; // the field of view in degrees float mNearPlaneDistance; // near plane distance float mFarPlaneDistance; // far plane distance float mAspectRatio; // width/height. TODO: Support separate pixel and viewport aspect ratios float4x4 mView; float4x4 mProjection; public: CPUTFrustum mFrustum; CPUTCamera(); ~CPUTCamera() {} void Update( float deltaSeconds=0.0f ) { // TODO: Do only if required (i.e. if dirty) mProjection = float4x4PerspectiveFovLH( mFov, mAspectRatio, mFarPlaneDistance, mNearPlaneDistance ); mView = inverse(*GetWorldMatrix()); mFrustum.InitializeFrustum(this); }; CPUTResult LoadCamera(CPUTConfigBlock *pBlock, int *pParentID); float4x4 *GetViewMatrix(void) { // Update(); We can't afford to do this every time we're asked for the view matrix. Caller needs to make sure camera is updated before entering render loop. return &mView; } const float4x4* GetProjectionMatrix(void) const { return &mProjection; } void SetProjectionMatrix(const float4x4 &projection) { mProjection = projection; } float GetAspectRatio() { return mAspectRatio; } float GetFov() { return mFov; } void SetAspectRatio(const float aspectRatio); void SetFov( const float fov ); float GetNearPlaneDistance() { return mNearPlaneDistance; } float GetFarPlaneDistance() { return mFarPlaneDistance; } void SetNearPlaneDistance( const float nearPlaneDistance ) { mNearPlaneDistance = nearPlaneDistance; } void SetFarPlaneDistance( const float farPlaneDistance ) { mFarPlaneDistance = farPlaneDistance; } void LookAt( float xx, float yy, float zz ); }; //----------------------------------------------------------------------------- class CPUTCameraController : public CPUTEventHandler { protected: CPUTRenderNode *mpCamera; float mfMoveSpeed; float mfLookSpeed; int mnPrevFrameX; int mnPrevFrameY; CPUTMouseState mPrevFrameState; public: CPUTCameraController() : mpCamera(NULL) , mnPrevFrameX(0) , mnPrevFrameY(0) , mfMoveSpeed(1.0f) , mfLookSpeed(1.0f) { } ~CPUTCameraController(){ SAFE_RELEASE(mpCamera);} void SetCamera(CPUTRenderNode *pCamera) { SAFE_RELEASE(mpCamera); mpCamera = pCamera; pCamera->AddRef(); } CPUTRenderNode *GetCamera(void) const { return mpCamera; } void SetMoveSpeed(float speed) { mfMoveSpeed = speed; } void SetLookSpeed(float speed) { mfLookSpeed = speed; } virtual void Update(float deltaSeconds=0.0f) = 0; }; // TODO: Move these implementations to the .cpp file. //----------------------------------------------------------------------------- class CPUTCameraControllerFPS : public CPUTCameraController { public: void Update( float deltaSeconds=0.0f); // TODO: Change to Update(deltaSeconds) and IsKeyDown() CPUTEventHandledCode HandleKeyboardEvent(CPUTKey key) { return CPUT_EVENT_UNHANDLED; } CPUTEventHandledCode HandleMouseEvent(int x, int y, int wheel, CPUTMouseState state); }; //----------------------------------------------------------------------------- class CPUTCameraControllerArcBall : public CPUTCameraController { public: void Update( float deltaSeconds=0.0f ) {} CPUTEventHandledCode HandleKeyboardEvent(CPUTKey key) { return CPUT_EVENT_UNHANDLED; } CPUTEventHandledCode HandleMouseEvent(int x, int y, int wheel, CPUTMouseState state); }; #endif //#ifndef __CPUTCamera_H__ ================================================ FILE: CPUT/CPUT/CPUTCheckbox.cpp ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #include "CPUTCheckbox.h" #include "CPUTGuiController.h" #include "CPUTText.h" CPUT_SIZE CPUTCheckbox::mpCheckboxTextureSizeList[CPUT_CHECKBOX_NUM_IMAGES_IN_CHECKBOX] = { {0,0},{0,0},{0,0} }; // texture atlas information float gAtlasWidthCheckbox = 256.0f; float gAtlasHeightCheckbox = 64.0f; // Pixel coordinates of the active-idle button image within the texture atlas int gUVLocationsCheckbox_active[] = { 109,3, // tl 124,3, // tr 109,17, // bl 124,17 // br }; // Pixel coordinates of the pressed button image within the texture atlas int gUVLocationsCheckbox_pressed[] = { 109,20, // tl 124,20, // tr 109,35, // bl 124,35 // br }; // Pixel coordinates of the disabled button image within the texture atlas int gUVLocationsCheckbox_disabled[] = { 109,39, // tl 124,39, // tr 109,54, // bl 124,54 // br }; // Floating point 0.0f - 1.0f UV coordinates in the texture atlas for each corner of the image float3 mpUVCoordsCheckbox_active[4]; float3 mpUVCoordsCheckbox_pressed[4]; float3 mpUVCoordsCheckbox_disabled[4]; // Constructor //----------------------------------------------------------------------------- CPUTCheckbox::CPUTCheckbox(const cString ControlText, CPUTControlID id, CPUTFont *pFont): mbMouseInside(false), mbStartedClickInside(false), mVertexStride(0), mVertexOffset(0), mpCheckboxText(NULL), mpFont(pFont) { // initialize the state variables InitialStateSet(); // save the control ID for callbacks mcontrolID = id; // store the font to be used by this control mpFont = pFont; // set as enabled CPUTControl::SetEnable(true); // register all the instance resources RegisterInstanceResources(); // set the control's text string SetText(ControlText); } // Initial state of the control's member variables //----------------------------------------------------------------------------- void CPUTCheckbox::InitialStateSet() { // state mcontrolType = CPUT_CHECKBOX; mControlState = CPUT_CONTROL_ACTIVE; mCheckboxState = CPUT_CHECKBOX_UNCHECKED; mCheckboxGuiState = CPUT_CHECKBOX_GUI_NEUTRAL; // control ID mcontrolID = 0; // size mControlDimensions.x=0; mControlDimensions.y=0; mControlDimensions.height=0; mControlDimensions.width=0; // default label SetText(_L("checkbox")); } // Destructor //------------------------------------------------------------------------------ CPUTCheckbox::~CPUTCheckbox() { UnRegisterInstanceResources(); } // Get the x/y window position of this control //----------------------------------------------------------------------------- void CPUTCheckbox::GetPosition(int &x, int &y) { x = mControlDimensions.x; y = mControlDimensions.y; } // Get checkbox selection state //----------------------------------------------------------------------------- CPUTCheckboxState CPUTCheckbox::GetCheckboxState() { return mCheckboxState; } // Set checkbox selection state //----------------------------------------------------------------------------- void CPUTCheckbox::SetCheckboxState(CPUTCheckboxState State) { // if state changed, save it, and recalculate the control if(State != mCheckboxState) { mCheckboxState = State; Recalculate(); } } // Returns the number of quads needed to draw this control //-------------------------------------------------------------------------------- unsigned int CPUTCheckbox::GetOutputVertexCount() { // A checkbox is always made of 1 quad. // // --- // | 1 | // --- // Calculation: 3 quads/triangle * 2 triangles/quad * 1 quad return 3*2; } // Register assets //----------------------------------------------------------------------------- CPUTResult CPUTCheckbox::RegisterStaticResources() { // calculate the floating point, 0.0f - 1.0f, UV coordinates of each of the 9 images that // make up a button. Do this for the active, pressed, and disabled states. for(int ii=0; ii<4; ii++) { mpUVCoordsCheckbox_active[ii].x = gUVLocationsCheckbox_active[2*ii]/gAtlasWidthCheckbox; mpUVCoordsCheckbox_active[ii].y = gUVLocationsCheckbox_active[2*ii+1]/gAtlasHeightCheckbox; mpUVCoordsCheckbox_pressed[ii].x = gUVLocationsCheckbox_pressed[2*ii]/gAtlasWidthCheckbox; mpUVCoordsCheckbox_pressed[ii].y = gUVLocationsCheckbox_pressed[2*ii+1]/gAtlasHeightCheckbox; mpUVCoordsCheckbox_disabled[ii].x = gUVLocationsCheckbox_disabled[2*ii]/gAtlasWidthCheckbox; mpUVCoordsCheckbox_disabled[ii].y = gUVLocationsCheckbox_disabled[2*ii+1]/gAtlasHeightCheckbox; } // calculate the width/height in pixels of each of the image slices // that makes up the checkbox images mpCheckboxTextureSizeList[0].width = gUVLocationsCheckbox_active[2] - gUVLocationsCheckbox_active[0]; mpCheckboxTextureSizeList[0].height = gUVLocationsCheckbox_active[5] - gUVLocationsCheckbox_active[1]; mpCheckboxTextureSizeList[1].width = gUVLocationsCheckbox_pressed[2] - gUVLocationsCheckbox_pressed[0]; mpCheckboxTextureSizeList[1].height = gUVLocationsCheckbox_pressed[5] - gUVLocationsCheckbox_pressed[1]; mpCheckboxTextureSizeList[2].width = gUVLocationsCheckbox_disabled[2] - gUVLocationsCheckbox_disabled[0]; mpCheckboxTextureSizeList[2].height = gUVLocationsCheckbox_disabled[5] - gUVLocationsCheckbox_disabled[1]; return CPUT_SUCCESS; } // Release all static resources - only do this if NO more checkbox controls // are used anywhere on the system //----------------------------------------------------------------------------- CPUTResult CPUTCheckbox::UnRegisterStaticResources() { return CPUT_SUCCESS; } // Register any per-instance resources for this checkbox //----------------------------------------------------------------------------- CPUTResult CPUTCheckbox::RegisterInstanceResources() { return CPUT_SUCCESS; } // Unregister the checkbox's instance resources //----------------------------------------------------------------------------- CPUTResult CPUTCheckbox::UnRegisterInstanceResources() { // delete the static text object SAFE_DELETE(mpCheckboxText); return CPUT_SUCCESS; } //CPUTEventHandler // Handle keyboard events - none for this control //-------------------------------------------------------------------------------- CPUTEventHandledCode CPUTCheckbox::HandleKeyboardEvent(CPUTKey key) { UNREFERENCED_PARAMETER(key); return CPUT_EVENT_UNHANDLED; } // Handle mouse events //----------------------------------------------------------------------------- CPUTEventHandledCode CPUTCheckbox::HandleMouseEvent(int x, int y, int wheel, CPUTMouseState state) { UNREFERENCED_PARAMETER(wheel); CPUTEventHandledCode handledCode = CPUT_EVENT_UNHANDLED; if((CPUT_CONTROL_INACTIVE == mControlState) || (false == mControlVisible) ) { mbMouseInside = false; return handledCode; } // if we're continuing to be pressed, move around with the mouse movement //(CPUT_CONTROL_PRESSED == mControlState ) if( (CPUT_CHECKBOX_GUI_PRESSED == mCheckboxGuiState) && (CPUT_MOUSE_LEFT_DOWN == state)) { return CPUT_EVENT_HANDLED; } // handle events occuring in the control if( ContainsPoint(x,y) ) { // did we start our click inside the button? if((state & CPUT_MOUSE_LEFT_DOWN) && (true == mbMouseInside)) { mbStartedClickInside = true; mCheckboxGuiState = CPUT_CHECKBOX_GUI_PRESSED; handledCode = CPUT_EVENT_HANDLED; // tell gui system this control image is now dirty // and needs to rebuild it's draw list mControlGraphicsDirty = true; } // did they click inside the button? if(!(state & CPUT_MOUSE_LEFT_DOWN) && (true == mbStartedClickInside) && (CPUT_CHECKBOX_GUI_PRESSED == mCheckboxGuiState)) { // set the GUI/mouse controller states handledCode = CPUT_EVENT_HANDLED; mCheckboxGuiState = CPUT_CHECKBOX_GUI_NEUTRAL; // toggle the checkbox state if(CPUT_CHECKBOX_UNCHECKED == mCheckboxState) { mCheckboxState = CPUT_CHECKBOX_CHECKED; } else if(CPUT_CHECKBOX_CHECKED == mCheckboxState) { mCheckboxState = CPUT_CHECKBOX_UNCHECKED; } // trigger the users callback mpCallbackHandler->HandleCallbackEvent(1, mcontrolID, (CPUTControl*) this); // tell gui system this control image is now dirty // and needs to rebuild it's draw list mControlGraphicsDirty = true; } if(!(state & CPUT_MOUSE_LEFT_DOWN)) { mbMouseInside = true; } } else { // we left the button mbMouseInside = false; mCheckboxGuiState = CPUT_CHECKBOX_GUI_NEUTRAL; mbStartedClickInside = false; } return handledCode; } //CPUTControl // set the upper-left position of the checkbox control (screen space coords) //----------------------------------------------------------------------------- void CPUTCheckbox::SetPosition(int x, int y) { // set the new position mControlDimensions.x = x; mControlDimensions.y = y; // recalculate the vertex buffer with new x/y coords Recalculate(); // move the static text along with the bitmap graphic int textX, textY; GetTextPosition(textX, textY); mpCheckboxText->SetPosition(textX, textY); } //----------------------------------------------------------------------------- void CPUTCheckbox::GetDimensions(int &width, int &height) { CalculateBounds(); width = mControlDimensions.width; height = mControlDimensions.height; } // Get the text label on this checkbox //-------------------------------------------------------------------------------- void CPUTCheckbox::GetText(cString &TextString) { if(mpCheckboxText) { mpCheckboxText->GetString(TextString); } } // Sets the text label on this checkbox //-------------------------------------------------------------------------------- void CPUTCheckbox::SetText(const cString String) { // create the static text object if it doesn't exist if(NULL == mpCheckboxText) { mpCheckboxText = new CPUTText(mpFont); } // set the static control's text mpCheckboxText->SetText(String); // move the text to the right spot int x,y; GetTextPosition(x,y); mpCheckboxText->SetPosition(x, y); // position or size may move - force a recalculation of this control's location // if it is managed by the auto-arrange function if(this->IsAutoArranged()) { mControlNeedsArrangmentResizing = true; } else { // control graphics have been updated mControlGraphicsDirty = true; } } // Enable/disable the control //-------------------------------------------------------------------------------- void CPUTCheckbox::SetEnable(bool in_bEnabled) { // chain to CPUTControl CPUTControl::SetEnable(in_bEnabled); // set the control's text to match mpCheckboxText->SetEnable(in_bEnabled); // recalculate control's quads Recalculate(); } // With the given window x/y coordinate, is that point in this control //----------------------------------------------------------------------------- bool CPUTCheckbox::ContainsPoint(int x, int y) { if( (x>mControlDimensions.x) && (y>mControlDimensions.y) && (x< (mControlDimensions.x+mControlDimensions.width)) && (y< (mControlDimensions.y+mControlDimensions.height)) ) { return true; } return false; } // Calculate the bounding rectangle for the control // For the checkbox it includes the checkbox image and the text //----------------------------------------------------------------------------- void CPUTCheckbox::CalculateBounds() { int textX, textY; int textWidth, textHeight; // get the text GetTextPosition(textX, textY); mpCheckboxText->GetDimensions(textWidth, textHeight); mControlDimensions.width = (textX - mControlDimensions.x ) + textWidth; mControlDimensions.height = textHeight; if(mpCheckboxTextureSizeList[0].height > textHeight) { mControlDimensions.height = mpCheckboxTextureSizeList[0].height; } } // Calculate the correct location to place the text label - usually to the // right of the image allowing for spacing/image/etc //-------------------------------------------------------------------------------- void CPUTCheckbox::GetTextPosition(int &x, int &y) { // get the dimensions of the string in pixels CPUT_RECT TextRect; mpCheckboxText->GetDimensions(TextRect.width, TextRect.height); // calculate a good spot for the text to be in relation to the checkbox bitmap x = mControlDimensions.x + mpCheckboxTextureSizeList[0].width + CPUT_CHECKBOX_PADDING; // move right far enough not to overlap the bitmap y = mControlDimensions.y + mpCheckboxTextureSizeList[0].height - TextRect.height; // try to center text top-to-bottom } // 'Draw' this control into the supplied vertex buffer object //-------------------------------------------------------------------------------- void CPUTCheckbox::DrawIntoBuffer(CPUTGUIVertex *pVertexBufferMirror, UINT *pInsertIndex, UINT pMaxBufferSize, CPUTGUIVertex *pTextVertexBufferMirror, UINT *pTextInsertIndex, UINT MaxTextVertexBufferSize) { if(!mControlVisible) { return; } if((NULL==pVertexBufferMirror) || (NULL==pInsertIndex)) { return; } // Do we have enough room to put this control into the output buffer? int VertexCopyCount = GetOutputVertexCount(); ASSERT( (pMaxBufferSize >= *pInsertIndex + VertexCopyCount), _L("Too many CPUT GUI controls for allocated GUI buffer. Allocated GUI vertex buffer is too small.\n\nIncrease CPUT_GUI_BUFFER_SIZE size.") ); switch(mControlState) { case CPUT_CONTROL_ACTIVE: // copy the active+idle button into the stream if(CPUT_CHECKBOX_UNCHECKED == mCheckboxState) { memcpy(&pVertexBufferMirror[*pInsertIndex], mpMirrorBufferActive, sizeof(CPUTGUIVertex)*6); } // copy the pressed button into the stream if(CPUT_CHECKBOX_CHECKED == mCheckboxState) { memcpy(&pVertexBufferMirror[*pInsertIndex], mpMirrorBufferPressed, sizeof(CPUTGUIVertex)*6); } break; case CPUT_CONTROL_INACTIVE: // copy the inactive button into the stream memcpy(&pVertexBufferMirror[*pInsertIndex], mpMirrorBufferDisabled, sizeof(CPUTGUIVertex)*6); break; default: // error! unknown state ASSERT(0,_L("CPUTCheckbox: Control is in unknown state")); return; } // move the index the correct number of floats to account // for 1 new quad, each quad with 6 verts in it (and each vert with 3+2 floats in it). *pInsertIndex+=6; // now do the text // draw the text if(mpCheckboxText) { mpCheckboxText->DrawIntoBuffer(pTextVertexBufferMirror, pTextInsertIndex, MaxTextVertexBufferSize); } // we'll mark the control as no longer being 'dirty' mControlGraphicsDirty = false; } // Recalculates the the control's image quads //------------------------------------------------------------------------ void CPUTCheckbox::Recalculate() { // active/idle AddQuadIntoMirrorBuffer(mpMirrorBufferActive, (float) mControlDimensions.x, (float) mControlDimensions.y, (float) mpCheckboxTextureSizeList[0].width, (float) mpCheckboxTextureSizeList[0].height, mpUVCoordsCheckbox_active[0], mpUVCoordsCheckbox_active[3] ); // pressed AddQuadIntoMirrorBuffer(mpMirrorBufferPressed, (float) mControlDimensions.x, (float)mControlDimensions.y, (float) mpCheckboxTextureSizeList[1].width, (float) mpCheckboxTextureSizeList[1].height, mpUVCoordsCheckbox_pressed[0], mpUVCoordsCheckbox_pressed[3] ); // disabled AddQuadIntoMirrorBuffer(mpMirrorBufferDisabled, (float) mControlDimensions.x, (float) mControlDimensions.y, (float) mpCheckboxTextureSizeList[2].width, (float) mpCheckboxTextureSizeList[2].height, mpUVCoordsCheckbox_disabled[0], mpUVCoordsCheckbox_disabled[3] ); // re-calculate the bounding box for the control used for hit-testing/sizing CalculateBounds(); // Mark this control as 'dirty' for drawing and inform the gui system that // it needs to re-calculate it's drawing buffer mControlGraphicsDirty = true; } // This generates a quad with the supplied coordinates/uv's/etc. //------------------------------------------------------------------------ void CPUTCheckbox::AddQuadIntoMirrorBuffer(CPUTGUIVertex *pMirrorBuffer, float x, float y, float w, float h, float3 uv1, float3 uv2 ) { CPUTColor4 color; color.r = 1.0f;color.g = 1.0f;color.b = 1.0f;color.a = 1.0f; pMirrorBuffer[0].Pos = float3( x + 0.0f, y + 0.0f, 1.0f); pMirrorBuffer[0].UV = float2(uv1.x, uv1.y); pMirrorBuffer[0].Color = color; pMirrorBuffer[1].Pos = float3( x + w, y + 0.0f, 1.0f); pMirrorBuffer[1].UV = float2(uv2.x, uv1.y); pMirrorBuffer[1].Color = color; pMirrorBuffer[2].Pos = float3( x + 0.0f, y + h, 1.0f); pMirrorBuffer[2].UV = float2(uv1.x, uv2.y); pMirrorBuffer[2].Color = color; pMirrorBuffer[3].Pos = float3( x + w, y + 0.0f, 1.0f); pMirrorBuffer[3].UV = float2(uv2.x, uv1.y); pMirrorBuffer[3].Color = color; pMirrorBuffer[4].Pos = float3( x + w, y + h, 1.0f); pMirrorBuffer[4].UV = float2(uv2.x, uv2.y); pMirrorBuffer[4].Color = color; pMirrorBuffer[5].Pos = float3( x + 0.0f, y +h, 1.0f); pMirrorBuffer[5].UV = float2(uv1.x, uv2.y); pMirrorBuffer[5].Color = color; } ================================================ FILE: CPUT/CPUT/CPUTCheckbox.h ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #ifndef __CPUTCHECKBOX_H__ #define __CPUTCHECKBOX_H__ #include "CPUTControl.h" #include "CPUTGuiController.h" // forward declarations class CPUTFont; class CPUTText; typedef enum CPUTCheckboxState { CPUT_CHECKBOX_UNCHECKED, CPUT_CHECKBOX_CHECKED, } CPUTCheckboxState; typedef enum CPUTCheckboxGUIState { CPUT_CHECKBOX_GUI_NEUTRAL, CPUT_CHECKBOX_GUI_PRESSED, } CPUTCheckboxGUIState; const int CPUT_CHECKBOX_NUM_IMAGES_IN_CHECKBOX=3; const int CPUT_CHECKBOX_PADDING=5; // padding (in pixels) between checkbox image and the label // Checkbox //----------------------------------------------------------------------------- class CPUTCheckbox:public CPUTControl { public: // button should self-register with the GuiController on create CPUTCheckbox(CPUTCheckbox& copy); CPUTCheckbox(const cString ControlText, CPUTControlID id, CPUTFont *pFont); virtual ~CPUTCheckbox(); // CPUTControl virtual void GetPosition(int &x, int &y); virtual unsigned int GetOutputVertexCount(); // CPUTCheckboxDX11 virtual CPUTCheckboxState GetCheckboxState(); void SetCheckboxState(CPUTCheckboxState State); //CPUTEventHandler virtual CPUTEventHandledCode HandleKeyboardEvent(CPUTKey key); virtual CPUTEventHandledCode HandleMouseEvent(int x, int y, int wheel, CPUTMouseState state); //CPUTControl virtual bool ContainsPoint(int x, int y); virtual void SetPosition(int x, int y); virtual void GetDimensions(int &width, int &height); virtual void SetText(const cString String); virtual void GetText(cString &TextString); virtual void SetEnable(bool bEnabled); // Register assets static CPUTResult RegisterStaticResources(); static CPUTResult UnRegisterStaticResources(); CPUTResult RegisterInstanceResources(); CPUTResult UnRegisterInstanceResources(); // CPUTCheckboxDX11 void DrawIntoBuffer(CPUTGUIVertex *pVertexBufferMirror, UINT *pInsertIndex, UINT pMaxBufferSize, CPUTGUIVertex *pTextVertexBufferMirror, UINT *pTextInsertIndex, UINT MaxTextVertexBufferSize); protected: CPUT_RECT mControlDimensions; CPUTCheckboxState mCheckboxState; CPUTCheckboxGUIState mCheckboxGuiState; // helper functions void InitialStateSet(); // new for uber-buffer version CPUTFont *mpFont; CPUTGUIVertex mpMirrorBufferActive[6]; CPUTGUIVertex mpMirrorBufferPressed[6]; CPUTGUIVertex mpMirrorBufferDisabled[6]; void Recalculate(); void AddQuadIntoMirrorBuffer(CPUTGUIVertex *pMirrorBuffer, float x, float y, float w, float h, float3 uv1, float3 uv2 ); // static varibles used by ALL checkbox controls static CPUT_SIZE mpCheckboxTextureSizeList[CPUT_CHECKBOX_NUM_IMAGES_IN_CHECKBOX]; // GUI state bool mbMouseInside; bool mbStartedClickInside; // instance variables for this particular checkbox UINT mVertexStride; UINT mVertexOffset; CPUTText *mpCheckboxText; // helper functions void GetTextPosition(int &x, int &y); void CalculateBounds(); }; #endif //#ifndef __CPUTCHECKBOX_H__ ================================================ FILE: CPUT/CPUT/CPUTComputeShaderDX11.cpp ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #include "CPUTComputeShaderDX11.h" #include "CPUTAssetLibraryDX11.h" CPUTComputeShaderDX11 *CPUTComputeShaderDX11::CreateComputeShader( const cString &name, ID3D11Device *pD3dDevice, const cString &shaderMain, const cString &shaderProfile ) { ID3DBlob *pCompiledBlob = NULL; ID3D11ComputeShader *pNewComputeShader = NULL; CPUTAssetLibraryDX11 *pAssetLibrary = (CPUTAssetLibraryDX11*)CPUTAssetLibrary::GetAssetLibrary(); CPUTResult result = pAssetLibrary->CompileShaderFromFile(name, shaderMain, shaderProfile, &pCompiledBlob); ASSERT( CPUTSUCCESS(result), _L("Error compiling compute shader:\n\n") ); // Create the compute shader // TODO: Move to compute shader class HRESULT hr = pD3dDevice->CreateComputeShader( pCompiledBlob->GetBufferPointer(), pCompiledBlob->GetBufferSize(), NULL, &pNewComputeShader ); ASSERT( SUCCEEDED(hr), _L("Error creating compute shader:\n\n") ); // cString DebugName = _L("CPUTAssetLibraryDX11::GetComputeShader ")+name; // CPUTSetDebugName(pNewComputeShader, DebugName); CPUTComputeShaderDX11 *pNewCPUTComputeShader = new CPUTComputeShaderDX11( pNewComputeShader, pCompiledBlob ); // add shader to library pAssetLibrary->AddComputeShader(name + shaderMain + shaderProfile, pNewCPUTComputeShader); // return the shader return pNewCPUTComputeShader; } //-------------------------------------------------------------------------------------- CPUTComputeShaderDX11 *CPUTComputeShaderDX11::CreateComputeShaderFromMemory( const cString &name, ID3D11Device *pD3dDevice, const cString &shaderMain, const cString &shaderProfile, const char *pShaderSource ) { ID3DBlob* pCompiledBlob = NULL; ID3D11ComputeShader* pNewComputeShader = NULL; CPUTAssetLibraryDX11 *pAssetLibrary = (CPUTAssetLibraryDX11*)CPUTAssetLibrary::GetAssetLibrary(); CPUTResult result = pAssetLibrary->CompileShaderFromMemory(pShaderSource, shaderMain, shaderProfile, &pCompiledBlob); ASSERT( CPUTSUCCESS(result), _L("Error compiling Compute shader:\n\n") ); // Create the Compute shader // TODO: Move to Compute shader class HRESULT hr = pD3dDevice->CreateComputeShader( pCompiledBlob->GetBufferPointer(), pCompiledBlob->GetBufferSize(), NULL, &pNewComputeShader ); ASSERT( SUCCEEDED(hr), _L("Error creating Compute shader:\n\n") ); // cString DebugName = _L("CPUTAssetLibraryDX11::GetComputeShader ")+name; // CPUTSetDebugName(pNewComputeShader, DebugName); CPUTComputeShaderDX11 *pNewCPUTComputeShader = new CPUTComputeShaderDX11( pNewComputeShader, pCompiledBlob ); // add shader to library pAssetLibrary->AddComputeShader(name + shaderMain + shaderProfile, pNewCPUTComputeShader); // pNewCPUTComputeShader->Release(); // We've added it to the library, so release our reference // return the shader (and blob) return pNewCPUTComputeShader; } ================================================ FILE: CPUT/CPUT/CPUTComputeShaderDX11.h ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #ifndef _CPUTCOMPUTESHADERDX11_H #define _CPUTCOMPUTESHADERDX11_H #include "CPUT.h" #include "CPUTShaderDX11.h" class CPUTComputeShaderDX11 : public CPUTShaderDX11 { protected: ID3D11ComputeShader *mpComputeShader; // Destructor is not public. Must release instead of delete. ~CPUTComputeShaderDX11(){ SAFE_RELEASE(mpComputeShader) } public: static CPUTComputeShaderDX11 *CreateComputeShader( const cString &name, ID3D11Device *pD3dDevice, const cString &shaderMain, const cString &shaderProfile ); static CPUTComputeShaderDX11 *CreateComputeShaderFromMemory( const cString &name, ID3D11Device *pD3dDevice, const cString &shaderMain, const cString &shaderProfile, const char *pShaderSource ); CPUTComputeShaderDX11() : mpComputeShader(NULL), CPUTShaderDX11(NULL) {} CPUTComputeShaderDX11(ID3D11ComputeShader *pD3D11ComputeShader, ID3DBlob *pBlob) : mpComputeShader(pD3D11ComputeShader), CPUTShaderDX11(pBlob) {} ID3DBlob *GetBlob() { return mpBlob; } ID3D11ComputeShader *GetNativeComputeShader() { return mpComputeShader; } }; #endif //_CPUTCOMPUTESHADER_H ================================================ FILE: CPUT/CPUT/CPUTConfigBlock.cpp ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #include "CPUTConfigBlock.h" #include "CPUTOSServicesWin.h" CPUTConfigEntry &CPUTConfigEntry::sNullConfigValue = CPUTConfigEntry(_L(""), _L("")); //---------------------------------------------------------------- static bool iswhite(char ch) { return ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n'; } template static void RemoveWhitespace(T &start, T &end) { while (start < end && iswhite(*start)) { ++start; } while (end > start && iswhite(*(end - 1))) { --end; } } //---------------------------------------------------------------- static bool ReadLine(const char **ppStart, const char **ppEnd, const char **ppCur) { const char *pCur = *ppCur; if (!*pCur) // check for EOF { return false; } // We're at the start of a line now, skip leading whitespace while (*pCur == ' ' || *pCur == '\t') { ++pCur; } *ppStart = pCur; // Forward to the end of the line and keep track of last non-whitespace char const char *pEnd = pCur; for (;;) { char ch = *pCur++; if (!ch) { --pCur; // terminating NUL isn't consumed break; } else if (ch == '\n') { break; } else if (!iswhite(ch)) { pEnd = pCur; } } *ppEnd = pEnd; *ppCur = pCur; return true; } //---------------------------------------------------------------- static const char *FindFirst(const char *start, const char *end, char ch) { const char *p = start; while (p < end && *p != ch) { ++p; } return p; } static const char *FindLast(const char *start, const char *end, char ch) { const char *p = end; while (--p >= start && *p != ch) { } return p; } static void AssignStr(cString &dest, const char *start, const char *end, _locale_t locale) { dest.clear(); if (end <= start) { return; } static const int NBUF = 64; wchar_t buf[NBUF]; int nb = 0; size_t len = end - start; size_t initial = len + 1; // assume most characters are 1-byte dest.reserve(initial); const char *p = start; while (p < end) { int len = _mbtowc_l(&buf[nb++], p, end - p, locale); if (len < 1) { break; } p += len; if (p >= end || nb >= NBUF) { dest.append(buf, nb); nb = 0; } } } //---------------------------------------------------------------- void CPUTConfigEntry::ValueAsFloatArray(float *pFloats, int count) { cString valueCopy = szValue; TCHAR *szOrigValue = (TCHAR*)valueCopy.c_str(); TCHAR *szNewValue = NULL; TCHAR *szCurrValue = wcstok_s(szOrigValue, _L(" "), &szNewValue); for(int clear = 0; clear < count; clear++) { pFloats[clear] = 0.0f; } for(int ii=0;ii= mnValueCount) { return NULL; } return &mpValues[nValueIndex]; } //---------------------------------------------------------------- CPUTConfigEntry *CPUTConfigBlock::AddValue(const cString &szName, const cString &szValue ) { cString szNameLower = szName; std::transform(szNameLower.begin(), szNameLower.end(), szNameLower.begin(), ::tolower); cString szValueLower = szValue; std::transform(szValueLower.begin(), szValueLower.end(), szValueLower.begin(), ::tolower); // TODO: What should we do if it already exists? CPUTConfigEntry *pEntry = &mpValues[mnValueCount++]; pEntry->szName = szNameLower; pEntry->szValue = szValueLower; return pEntry; } //---------------------------------------------------------------- CPUTConfigEntry *CPUTConfigBlock::GetValueByName(const cString &szName) { for(int ii=0; iiOpenFile(szFilename, &pFile); if(CPUTFAILED(result)) { return result; } _locale_t locale = _get_current_locale(); /* Determine file size */ fseek(pFile, 0, SEEK_END); int nBytes = ftell(pFile); // for text files, this is an overestimate fseek(pFile, 0, SEEK_SET); /* Read the whole thing */ char *pFileContents = new char[nBytes + 1]; nBytes = (int)fread(pFileContents, 1, nBytes, pFile); fclose(pFile); pFileContents[nBytes] = 0; // add 0-terminator /* Count the number of blocks */ const char *pCur = pFileContents; const char *pStart, *pEnd; while(ReadLine(&pStart, &pEnd, &pCur)) { const char *pOpen = FindFirst(pStart, pEnd, '['); const char *pClose = FindLast(pOpen + 1, pEnd, ']'); if (pOpen < pClose) { // This line is a valid block header mnBlockCount++; } } // For files that don't have any blocks, just add the entire file to one block if(mnBlockCount == 0) { mnBlockCount = 1; } pCur = pFileContents; mpBlocks = new CPUTConfigBlock[mnBlockCount]; pCurrBlock = mpBlocks; /* Find the first block first */ while(ReadLine(&pStart, &pEnd, &pCur)) { const char *pOpen = FindFirst(pStart, pEnd, '['); const char *pClose = FindLast(pOpen + 1, pEnd, ']'); if (pOpen < pClose) { // This line is a valid block header pCurrBlock = mpBlocks + nCurrBlock++; AssignStr(pCurrBlock->mszName, pOpen + 1, pClose, locale); std::transform(pCurrBlock->mszName.begin(), pCurrBlock->mszName.end(), pCurrBlock->mszName.begin(), ::tolower); } else if (pStart < pEnd) { // It's a value if (pCurrBlock == NULL) { continue; } const char *pEquals = FindFirst(pStart, pEnd, '='); if (pEquals == pEnd) { // No value, just a key, save it anyway // Optimistically, we assume it's new cString &name = pCurrBlock->mpValues[pCurrBlock->mnValueCount].szName; AssignStr(name, pStart, pEnd, locale); bool dup = false; for(int ii=0;iimnValueCount;++ii) { if(!pCurrBlock->mpValues[ii].szName.compare(name)) { dup = true; break; } } if(!dup) { pCurrBlock->mnValueCount++; } } else { const char *pNameStart = pStart; const char *pNameEnd = pEquals; const char *pValStart = pEquals + 1; const char *pValEnd = pEnd; RemoveWhitespace(pNameStart, pNameEnd); RemoveWhitespace(pValStart, pValEnd); // Optimistically assume the name is new cString &name = pCurrBlock->mpValues[pCurrBlock->mnValueCount].szName; AssignStr(name, pNameStart, pNameEnd, locale); std::transform(name.begin(), name.end(), name.begin(), ::tolower); bool dup = false; for(int ii=0;iimnValueCount;++ii) { if(!pCurrBlock->mpValues[ii].szName.compare(name)) { dup = true; break; } } if(!dup) { AssignStr(pCurrBlock->mpValues[pCurrBlock->mnValueCount].szValue, pValStart, pValEnd, locale); pCurrBlock->mnValueCount++; } } } } delete[] pFileContents; return CPUT_SUCCESS; } //---------------------------------------------------------------- CPUTConfigBlock *CPUTConfigFile::GetBlock(int nBlockIndex) { if(nBlockIndex >= mnBlockCount || nBlockIndex < 0) { return NULL; } return &mpBlocks[nBlockIndex]; } //---------------------------------------------------------------- CPUTConfigBlock *CPUTConfigFile::GetBlockByName(const cString &szBlockName) { cString szString = szBlockName; std::transform(szString.begin(), szString.end(), szString.begin(), ::tolower); for(int ii=0; ii // for std::transform #if !defined(UNICODE) && !defined(_UNICODE) #define fgetws fgets #define swscanf_s sscanf_s #define wcstok_s strtok_s #define wcsncmp strncmp #define _wtoi atoi #define _wtol atol #endif typedef UINT UINT; class CPUTConfigEntry { private: cString szName; cString szValue; friend class CPUTConfigBlock; friend class CPUTConfigFile; public: CPUTConfigEntry() {} CPUTConfigEntry(const cString &name, const cString &value): szName(name), szValue(value){}; static CPUTConfigEntry &sNullConfigValue; const cString & NameAsString(void){ return szName;}; const cString & ValueAsString(void){ return szValue; } bool IsValid(void){ return !szName.empty(); } float ValueAsFloat(void) { float fValue=0; int retVal; retVal=swscanf_s(szValue.c_str(), _L("%g"), &fValue ); // float (regular float, or E exponentially notated float) ASSERT(0!=retVal, _L("ValueAsFloat - value specified is not a float")); return fValue; } int ValueAsInt(void) { int nValue=0; int retVal; retVal=swscanf_s(szValue.c_str(), _L("%d"), &nValue ); // signed int (NON-hex) ASSERT(0!=retVal, _L("ValueAsInt - value specified is not a signed int")); return nValue; } UINT ValueAsUint(void) { UINT nValue=0; int retVal; retVal=swscanf_s(szValue.c_str(), _L("%u"), &nValue ); // unsigned int ASSERT(0!=retVal, _L("ValueAsUint - value specified is not a UINT")); return nValue; } bool ValueAsBool(void) { return (szValue.compare(_L("true")) == 0) || (szValue.compare(_L("1")) == 0) || (szValue.compare(_L("t")) == 0); } void ValueAsFloatArray(float *pFloats, int count); }; class CPUTConfigBlock { public: CPUTConfigBlock(); ~CPUTConfigBlock(); CPUTConfigEntry *AddValue(const cString &szName, const cString &szValue); CPUTConfigEntry *GetValue(int nValueIndex); CPUTConfigEntry *GetValueByName(const cString &szName); const cString &GetName(void); int GetNameValue(void); int ValueCount(void); bool IsValid() { return mnValueCount > 0; } private: CPUTConfigEntry mpValues[64]; CPUTConfigEntry mName; cString mszName; int mnValueCount; friend class CPUTConfigFile; }; class CPUTConfigFile { public: CPUTConfigFile(); ~CPUTConfigFile(); CPUTResult LoadFile(const cString &szFilename); CPUTConfigBlock *GetBlock(int nBlockIndex); CPUTConfigBlock *GetBlockByName(const cString &szBlockName); int BlockCount(void); private: CPUTConfigBlock *mpBlocks; int mnBlockCount; }; #endif //#ifndef __CPUTPARSELIBRARY_H__ ================================================ FILE: CPUT/CPUT/CPUTControl.cpp ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #include "CPUTControl.h" // Constructor //------------------------------------------------------------------------------ CPUTControl::CPUTControl():mControlVisible(true), mControlGraphicsDirty(false), mControlAutoArranged(true), mControlNeedsArrangmentResizing(true), mhotkey(KEY_NONE), mcontrolType(CPUT_CONTROL_UNKNOWN), mcontrolID(0), mpCallbackHandler(NULL), mControlState(CPUT_CONTROL_ACTIVE) { } // Destructor //------------------------------------------------------------------------------ CPUTControl::~CPUTControl() { } // Control type/identifier routines that have a common implementation for all controls // Sets the control's ID used for identification purposes (hopefully unique) //------------------------------------------------------------------------------ void CPUTControl::SetControlID(CPUTControlID id) { mcontrolID = id; } // Get the ID for this control //------------------------------------------------------------------------------ CPUTControlID CPUTControl::GetControlID() { return mcontrolID; } // Get the type of control this is (button/dropdown/etc) //------------------------------------------------------------------------------ CPUTControlType CPUTControl::GetType() { return mcontrolType; } // Set callback handler //------------------------------------------------------------------------------ void CPUTControl::SetControlCallback(CPUTCallbackHandler *pHandler) { mpCallbackHandler = pHandler; } // set whether controls is visible or not (it is still there, but not visible) //------------------------------------------------------------------------------ void CPUTControl::SetVisibility(bool bVisible) { mControlVisible = bVisible; } // visibility state //------------------------------------------------------------------------------ bool CPUTControl::IsVisible() { return mControlVisible; } // Set the hot key for keyboard events for this control //------------------------------------------------------------------------------ void CPUTControl::SetHotkey(CPUTKey hotKey) { mhotkey = hotKey; } // Get the hot key set for this control //------------------------------------------------------------------------------ CPUTKey CPUTControl::GetHotkey() { return mhotkey; } // Should this control be auto-arranged? //------------------------------------------------------------------------------ void CPUTControl::SetAutoArranged(bool bIsAutoArranged) { mControlAutoArranged = bIsAutoArranged; } //------------------------------------------------------------------------------ bool CPUTControl::IsAutoArranged() { return mControlAutoArranged; } // Set the control to enabled or greyed out //------------------------------------------------------------------------------ void CPUTControl::SetEnable(bool bEnabled) { if(!bEnabled) { mControlState = CPUT_CONTROL_INACTIVE; } else { mControlState = CPUT_CONTROL_ACTIVE; } } // Return bool if the control is enabled/greyed out //------------------------------------------------------------------------------ bool CPUTControl::IsEnabled() { if(mControlState == CPUT_CONTROL_INACTIVE) { return false; } return true; } ================================================ FILE: CPUT/CPUT/CPUTControl.h ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #ifndef __CPUTCONTROL_H__ #define __CPUTCONTROL_H__ #include #include "CPUT.h" #include "CPUTEventHandler.h" // internal types // TODO: Why are these int and not float? struct CPUT_SIZE { int width; int height; }; struct CPUT_POINT { int x; int y; }; struct CPUT_RECT { int x; int y; int width; int height; }; // This is a list of all the known control types // if you make a new control, be sure to add it here // so the GUI controller can manage it properly enum CPUTControlType { CPUT_CONTROL_UNKNOWN, CPUT_BUTTON, CPUT_CHECKBOX, CPUT_DROPDOWN, CPUT_SLIDER, CPUT_STATIC, CPUT_PANE, }; // state of the control typedef enum CPUTGUIControlState { CPUT_CONTROL_ACTIVE, CPUT_CONTROL_INACTIVE, } CPUTGUIControlState; // control/event ID defines typedef unsigned int UINT; typedef UINT CPUTControlID; typedef UINT CPUTEventID; const UINT CPUT_CONTROL_ID_INVALID=(UINT)-1; // forward declaration class CPUTCallbackHandler; // CPUTControl base class // This is basically a virtual class that defines the common calls for manipulation // controls. Individual controls implement this interface. class CPUTControl:public CPUTEventHandler { public: CPUTControl(); virtual ~CPUTControl(); // Control type virtual void SetControlID(CPUTControlID id); virtual CPUTControlID GetControlID(); virtual CPUTControlType GetType(); // Set what object to call back on events void SetControlCallback(CPUTCallbackHandler *pHandler); // Graphical state manipulation virtual void GetPosition(int &x, int &y)=0; virtual void SetPosition(int x, int y)=0; virtual void GetDimensions(int &width, int &height)=0; virtual bool ContainsPoint(int x, int y)=0; virtual void SetVisibility(bool bVisible); virtual bool IsVisible(); virtual void SetEnable(bool in_bEnabled); virtual bool IsEnabled(); virtual void SetAutoArranged(bool bIsAutoArranged); virtual bool IsAutoArranged(); // keyboard event hotkey for this control virtual void SetHotkey(CPUTKey hotKey); virtual CPUTKey GetHotkey(); // buffer management virtual unsigned int GetQuadCount() {return 0;} virtual bool ControlGraphicsDirty() {return mControlGraphicsDirty;} virtual bool ControlResizedItself() {return mControlNeedsArrangmentResizing;} virtual void ControlResizingHandled() { mControlNeedsArrangmentResizing = false;} virtual void DrawIntoBuffer(float *pVertexBufferMirror, int *pInsertIndex, int pMaxBufferSize) {return;} protected: bool mControlVisible; bool mControlAutoArranged; bool mControlGraphicsDirty; bool mControlNeedsArrangmentResizing; CPUTKey mhotkey; CPUTControlType mcontrolType; CPUTControlID mcontrolID; CPUTCallbackHandler *mpCallbackHandler; CPUTGUIControlState mControlState; }; #endif //#ifndef __CPUTCONTROL_H__ ================================================ FILE: CPUT/CPUT/CPUTDomainShaderDX11.cpp ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #include "CPUTDomainShaderDX11.h" #include "CPUTAssetLibraryDX11.h" CPUTDomainShaderDX11 *CPUTDomainShaderDX11::CreateDomainShader( const cString &name, ID3D11Device *pD3dDevice, const cString &shaderMain, const cString &shaderProfile ) { ID3DBlob *pCompiledBlob = NULL; ID3D11DomainShader *pNewDomainShader = NULL; CPUTAssetLibraryDX11 *pAssetLibrary = (CPUTAssetLibraryDX11*)CPUTAssetLibrary::GetAssetLibrary(); CPUTResult result = pAssetLibrary->CompileShaderFromFile(name, shaderMain, shaderProfile, &pCompiledBlob); ASSERT( CPUTSUCCESS(result), _L("Error compiling Domain shader:\n\n") ); // Create the Domain shader // TODO: Move to Domain shader class HRESULT hr = pD3dDevice->CreateDomainShader( pCompiledBlob->GetBufferPointer(), pCompiledBlob->GetBufferSize(), NULL, &pNewDomainShader ); ASSERT( SUCCEEDED(hr), _L("Error creating Domain shader:\n\n") ); // cString DebugName = _L("CPUTAssetLibraryDX11::GetDomainShader ")+name; // CPUTSetDebugName(pNewDomainShader, DebugName); CPUTDomainShaderDX11 *pNewCPUTDomainShader = new CPUTDomainShaderDX11( pNewDomainShader, pCompiledBlob ); // add shader to library pAssetLibrary->AddDomainShader(name, pNewCPUTDomainShader); // pNewCPUTDomainShader->Release(); // We've added it to the library, so release our reference // return the shader (and blob) return pNewCPUTDomainShader; } //-------------------------------------------------------------------------------------- CPUTDomainShaderDX11 *CPUTDomainShaderDX11::CreateDomainShaderFromMemory( const cString &name, ID3D11Device *pD3dDevice, const cString &shaderMain, const cString &shaderProfile, const char *pShaderSource ) { ID3DBlob *pCompiledBlob = NULL; ID3D11DomainShader *pNewDomainShader = NULL; CPUTAssetLibraryDX11 *pAssetLibrary = (CPUTAssetLibraryDX11*)CPUTAssetLibrary::GetAssetLibrary(); CPUTResult result = pAssetLibrary->CompileShaderFromMemory(pShaderSource, shaderMain, shaderProfile, &pCompiledBlob); ASSERT( CPUTSUCCESS(result), _L("Error compiling Domain shader:\n\n") ); // Create the Domain shader // TODO: Move to Domain shader class HRESULT hr = pD3dDevice->CreateDomainShader( pCompiledBlob->GetBufferPointer(), pCompiledBlob->GetBufferSize(), NULL, &pNewDomainShader ); ASSERT( SUCCEEDED(hr), _L("Error creating Domain shader:\n\n") ); // cString DebugName = _L("CPUTAssetLibraryDX11::GetDomainShader ")+name; // CPUTSetDebugName(pNewDomainShader, DebugName); CPUTDomainShaderDX11 *pNewCPUTDomainShader = new CPUTDomainShaderDX11( pNewDomainShader, pCompiledBlob ); // add shader to library pAssetLibrary->AddDomainShader(name + shaderMain + shaderProfile, pNewCPUTDomainShader); // pNewCPUTDomainShader->Release(); // We've added it to the library, so release our reference // return the shader (and blob) return pNewCPUTDomainShader; } ================================================ FILE: CPUT/CPUT/CPUTDomainShaderDX11.h ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #ifndef _CPUTDOMAINSHADERDX11_H #define _CPUTDOMAINSHADERDX11_H #include "CPUT.h" #include "CPUTShaderDX11.h" class CPUTDomainShaderDX11 : public CPUTShaderDX11 { protected: ID3D11DomainShader *mpDomainShader; // Destructor is not public. Must release instead of delete. ~CPUTDomainShaderDX11(){ SAFE_RELEASE(mpDomainShader) } public: static CPUTDomainShaderDX11 *CreateDomainShader( const cString &name, ID3D11Device *pD3dDevice, const cString &shaderMain, const cString &shaderProfile ); static CPUTDomainShaderDX11 *CreateDomainShaderFromMemory( const cString &name, ID3D11Device *pD3dDevice, const cString &shaderMain, const cString &shaderProfile, const char *pShaderSource ); CPUTDomainShaderDX11() : mpDomainShader(NULL), CPUTShaderDX11(NULL) {} CPUTDomainShaderDX11(ID3D11DomainShader *pD3D11DomainShader, ID3DBlob *pBlob) : mpDomainShader(pD3D11DomainShader), CPUTShaderDX11(pBlob) {} ID3DBlob *GetBlob() { return mpBlob; } ID3D11DomainShader *GetNativeDomainShader() { return mpDomainShader; } }; #endif //_CPUTDOMAINSHADER_H ================================================ FILE: CPUT/CPUT/CPUTDropdown.cpp ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #include "CPUTDropdown.h" #include "CPUTText.h" // uber-buffer CPUT_SIZE CPUTDropdown::mpDropdownIdleImageSizeList[] = { {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0} }; CPUT_SIZE CPUTDropdown::mpDropdownDisabledSizeList[] = { {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0} }; // texture atlas information float gDropdownAtlasWidth = 256.0f; float gDropdownAtlasHeight = 64.0f; // Texture atlas coordinates of the active-idle dropdown control int gDropdownUVLocations_active[] = { 140,42, 143,45, // lt 140,47, 143,48, // lm 140,50, 143,53, // lb 145,42, 146,45, // mt 145,47, 146,48, // mm 145,50, 146,53, // mb 148,42, 151,45, // rt 148,47, 151,48, // rm 148,50, 151,53, // rb 139,21, 159,41, // button 163,21, 183,41, // button down 230,21, 231,22 // highlight }; // Texture atlas coordinates of the disabled dropdown control int gDropdownUVLocations_disabled[] = { 155,42, 158,45, // lt 155,47, 158,48, // lm 155,50, 158,53, // lb 160,42, 161,45, // mt 160,47, 161,48, // mm 160,50, 161,53, // mb 163,42, 166,45, // rt 163,47, 166,48, // rm 163,50, 166,53, // rb 186,21, 206,41, // button 208,21, 228,41, // button down 236,21, 237,22 // highlight }; const int DropdownQuadCount = 12; float3 mpDropdownUVCoords_active[DropdownQuadCount*2]; float3 mpDropdownUVCoords_disabled[DropdownQuadCount*2]; int CTrayLM = 1; int CTrayLB = 2; int CTrayMM = 4; int CTrayMB = 5; int CTrayRM = 7; int CTrayRB = 8; int CTrayHighlight = 11; // Constructor //----------------------------------------------------------------------------- CPUTDropdown::CPUTDropdown(const cString ControlName, CPUTControlID id, CPUTFont *pFont):mVertexStride(0), mVertexOffset(0), mSelectedItemIndex(0), mConfirmedSelectedItemIndex(0), mbSizeDirty(false), mbMouseInside(false), mbStartedClickInside(false), mbStartedClickInsideTray(false), mRevertItem((UINT)-1), mpMirrorBufferActive(NULL), mpMirrorBufferDisabled(NULL), mpButtonText(NULL), mpFont(pFont) { // initialize the state variables InitialStateSet(); // save the control ID for callbacks mcontrolID = id; // set as enabled CPUTControl::SetEnable(true); // this is a copy of whatever item is selected mpSelectedItemCopy = new CPUTText(pFont); // clear the button selected area rect mButtonRect.x=0; mButtonRect.y=0; mButtonRect.width=0; mButtonRect.height=0; mTrayDimensions.x=0; mTrayDimensions.y=0; mTrayDimensions.width=0; mTrayDimensions.height=0; // set the string to display with the slider AddSelectionItem(ControlName, true); // Register any instance resources RegisterInstanceResources(); } // Initial state of the control's member variables //----------------------------------------------------------------------------- void CPUTDropdown::InitialStateSet() { // set the type mcontrolType = CPUT_DROPDOWN; mControlState = CPUT_CONTROL_ACTIVE; mControlGuiState = CPUT_DROPDOWN_GUI_MOUSE_NEUTRAL; // save the control ID for callbacks mcontrolID = 0; // default location mControlDimensions.x=0; mControlDimensions.y=0; mControlDimensions.height=0; mControlDimensions.width=0; } // Destructor //------------------------------------------------------------------------------ CPUTDropdown::~CPUTDropdown() { UnRegisterInstanceResources(); } // Return xy position of control //----------------------------------------------------------------------------- void CPUTDropdown::GetPosition(int &x, int &y) { x = mControlDimensions.x; y = mControlDimensions.y; } // Returns the number of quads needed to draw this control //-------------------------------------------------------------------------------- unsigned int CPUTDropdown::GetOutputVertexCount() { // A dropdown is always made of: // - 9 quads for the display area // - 1 quad for the tray button // - 6 quads for the tray itself // - 1 quad for the highlighted area // Quads 2,4,5,6, and 8 'stretch' in height and/or width to fit the // static text/content inside the button // // ---+------------------+--- // | 1 | 4 | 7 | // |---+------------------+---| // | | +----+ | | // | 2 | 5 | 10 | | 8 | // | | +----+ | | // |---+------------------+---| // | 3 | 6 | 9 | // ---+------------------+--- // | | | | // | 11| 12 | 13| // | | | | // |---+--------------+---| // | 14| 15 | 16| // ---+--------------+--- // // calculation: (3 verts/triangle * 2 triangles/quad) * (9 + 1 + 6 + 1 quads) return (3*2) * (9+1+6+1); } // Load and register all instance variables //----------------------------------------------------------------------------- CPUTResult CPUTDropdown::RegisterInstanceResources() { return CPUT_SUCCESS; } // Release all instance resources (vertex buffer quads/etc) //----------------------------------------------------------------------------- CPUTResult CPUTDropdown::UnRegisterInstanceResources() { // delete the list of selection items (CPUTTexts) for(UINT i=0; iSetText(Item, 0.35f); // set the text slightly higher than the 0.5 normal mode mpListOfSelectableItems.push_back(pNewItem); // Should this be the currently selected item? if(bIsSelected) { mSelectedItemIndex = (int) mpListOfSelectableItems.size()-1; // set the highlighted element to this item mConfirmedSelectedItemIndex = mSelectedItemIndex; // set the hard-selected item to this as well } // was this the first item added to an empty list? if( 1==mpListOfSelectableItems.size() ) { mSelectedItemIndex=0; mConfirmedSelectedItemIndex=0; } // mark that a resize of dropdown selection box is needed mbSizeDirty = true; Recalculate(); // position or size may move - force a recalculation of this control's location // if it is managed by the auto-arrange function if(this->IsAutoArranged()) { mControlNeedsArrangmentResizing = true; } return CPUT_SUCCESS; } // Return the number of items in the dropdown list //----------------------------------------------------------------------------- void CPUTDropdown::NumberOfSelectableItems(UINT &count) { count = (UINT) mpListOfSelectableItems.size(); } // Return the index of the currently selected item //----------------------------------------------------------------------------- void CPUTDropdown::GetSelectedItem(UINT &index) { //index = mSelectedItemIndex+1; index = mConfirmedSelectedItemIndex; } // Fill user's buffer with string of the currently selected item //----------------------------------------------------------------------------- void CPUTDropdown::GetSelectedItem(cString &Item) { if(-1!=mSelectedItemIndex) { //mpListOfSelectableItems[mSelectedItemIndex]->GetString(Item); mpListOfSelectableItems[mConfirmedSelectedItemIndex]->GetString(Item); } } // Sets which item in the dropdown is currently selected //----------------------------------------------------------------------------- void CPUTDropdown::SetSelectedItem(const UINT index) { if( (index>0) && (index <= mpListOfSelectableItems.size() )) { mSelectedItemIndex = (index-1); mConfirmedSelectedItemIndex = mSelectedItemIndex; // set the hard-selected item to this as well } // Recalculate and mark control for redraw Recalculate(); } // Sets which item in the dropdown is currently highlighted //----------------------------------------------------------------------------- void CPUTDropdown::SetHighlightedItem(const UINT index) { if( (index>0) && (index <= mpListOfSelectableItems.size() )) { mSelectedItemIndex = (index-1); } // Recalculate and mark control for redraw Recalculate(); } // Delete an item at a specific index //----------------------------------------------------------------------------- void CPUTDropdown::DeleteSelectionItem(const UINT index) { if((0==index) || (index>mpListOfSelectableItems.size())) return; // list is zero based, not 1 based UINT itemIndex = index-1; // physically delete the text object CPUTText *pItem = mpListOfSelectableItems[itemIndex]; SAFE_DELETE(pItem); // remove the item from the list mpListOfSelectableItems.erase(mpListOfSelectableItems.begin()+itemIndex); // if we're deleting the selected item or any above it // move the current selection down by oneq if(mSelectedItemIndex > itemIndex) { mSelectedItemIndex--; mConfirmedSelectedItemIndex = mSelectedItemIndex; } if(mSelectedItemIndex >= mpListOfSelectableItems.size()) { mSelectedItemIndex = (UINT) mpListOfSelectableItems.size()-1; mConfirmedSelectedItemIndex = mSelectedItemIndex; } // resize the dropdown selection box mbSizeDirty = true; Recalculate(); // position or size may move - force a recalculation of this control's location // if it is managed by the auto-arrange function if(this->IsAutoArranged()) { mControlNeedsArrangmentResizing = true; } } // Delete an item with string // Only deletes the first occurance of the string, you'd need to call multiple // times to delete each occurance //----------------------------------------------------------------------------- void CPUTDropdown::DeleteSelectionItem(const cString string) { cString itemText; for(UINT i=0; iGetString(itemText); if(0==itemText.compare(string)) { DeleteSelectionItem(i+1); break; } } Recalculate(); } // Handle mouse events //----------------------------------------------------------------------------- CPUTEventHandledCode CPUTDropdown::HandleMouseEvent(int x, int y, int wheel, CPUTMouseState state) { UNREFERENCED_PARAMETER(wheel); CPUTEventHandledCode handledCode = CPUT_EVENT_UNHANDLED; // just return if the control is disabled or invisible if((CPUT_CONTROL_INACTIVE == mControlState) || (false == mControlVisible) ) { mbMouseInside = false; return handledCode; } // tray is down, commence to selecting if(CPUT_DROPDOWN_GUI_MOUSE_PRESSED == mControlGuiState) { if(ContainsPointTrayArea(x, y)) { // tray is open // float the selection up/down based on mouse position, making // whichever item the mouse is over the selected item CPUT_RECT inner, outer; CalculateTrayRect(inner, outer); if( (x>outer.x) && (xinner.y) && (yHandleCallbackEvent(1, mcontrolID, (CPUTControl*) this); // remember this item as the last selected item GetSelectedItem(mRevertItem); } // anything in the tray counts as handling the mouse event return CPUT_EVENT_HANDLED; } } // handle just the readout area if(ContainsPointReadoutArea(x,y)) { // click started outside this control - ignore it if( (false == mbMouseInside ) && (CPUT_MOUSE_LEFT_DOWN == state) ) { return handledCode; } // clicked and dragging around inside the control itself - ignore if( (true == mbStartedClickInside ) && (CPUT_MOUSE_LEFT_DOWN == state) ) { return CPUT_EVENT_HANDLED; } mbMouseInside = true; if( !(CPUT_MOUSE_LEFT_DOWN == state)) { mbStartedClickInside = false; mbStartedClickInsideTray = false; } if( CPUT_MOUSE_LEFT_DOWN == state ) { if(CPUT_DROPDOWN_GUI_MOUSE_NEUTRAL == mControlGuiState) { mbStartedClickInside = true; mbStartedClickInsideTray = true; // get the item to revert to GetSelectedItem(mRevertItem); // toggle tray state mControlGuiState = CPUT_DROPDOWN_GUI_MOUSE_PRESSED; Recalculate(); } else { mbStartedClickInside = true; mbStartedClickInsideTray = true; //mControlState = CPUT_CONTROL_ACTIVE; mControlGuiState = CPUT_DROPDOWN_GUI_MOUSE_NEUTRAL; SetSelectedItem(mRevertItem); mRevertItem=(UINT)-1; Recalculate(); } return CPUT_EVENT_HANDLED; } } else if(CPUT_MOUSE_LEFT_DOWN == state) { // clicked outside the control // if tray is down, restore previous selection and disable // display of the tray if(CPUT_DROPDOWN_GUI_MOUSE_NEUTRAL != mControlGuiState) { mControlGuiState = CPUT_DROPDOWN_GUI_MOUSE_NEUTRAL; SetSelectedItem(mRevertItem+1); mRevertItem=(UINT)-1; Recalculate(); } mbStartedClickInside = false; mbMouseInside = false; return CPUT_EVENT_UNHANDLED; } return handledCode; } // Register static textures/etc for this control //----------------------------------------------------------------------------- CPUTResult CPUTDropdown::RegisterStaticResources() { // calculate the UV coordinates of each of the 12 images that // make up a button. Do this for the active, pressed, and disabled states. for(int ii=0; iiGetDimensions(textDimensions.width, textDimensions.height); } x = inner.x; y = (int) (inner.y + inner.height/2.0f - textDimensions.height/2.0f + CPUT_DROPDOWN_TEXT_PADDING/2.0f); } // //----------------------------------------------------------------------------- void CPUTDropdown::CalculateMaxItemSize(int &width, int &height) { width=0; height=0; if(mpListOfSelectableItems.size()) { for(UINT i=0; iGetDimensions(rect.width, rect.height); if(width < rect.width) { width = rect.width; } if(height < rect.height) { height = rect.height; } } } else { // give dropdown a nice 'minimum' size if there is nothing mpSelectedItemCopy->SetText(_L(" ")); mpSelectedItemCopy->GetDimensions(width, height); } } // //----------------------------------------------------------------------------- void CPUTDropdown::CalculateTrayRect(CPUT_RECT &inner, CPUT_RECT &outer) { int maxItemWidth, maxItemHeight; CalculateMaxItemSize(maxItemWidth, maxItemHeight); CPUT_RECT innerReadout, outerReadout; CalculateReadoutRect(innerReadout, outerReadout); // locations outer.x = outerReadout.x + 5; outer.y = outerReadout.y + outerReadout.height; inner.x = outer.x + mpDropdownIdleImageSizeList[CLeftMid].width; inner.y = outer.y + CPUT_DROPDOWN_TEXT_PADDING; // dimension inner.width = maxItemWidth + 2*CPUT_DROPDOWN_TEXT_PADDING; inner.height = (int)((mpListOfSelectableItems.size())*(maxItemHeight+2*CPUT_DROPDOWN_TEXT_PADDING)); outer.width = inner.width + mpDropdownIdleImageSizeList[CLeftMid].width + mpDropdownIdleImageSizeList[CRightMid].width; outer.height = inner.height + mpDropdownIdleImageSizeList[CLeftBot].height; } // Enable/disable the control //-------------------------------------------------------------------------------- void CPUTDropdown::SetEnable(bool in_bEnabled) { if(in_bEnabled) { mControlState = CPUT_CONTROL_ACTIVE; } else { mControlState = CPUT_CONTROL_INACTIVE; } // set the control's text to match mpSelectedItemCopy->SetEnable(in_bEnabled); // recalculate control's quads Recalculate(); } // Get enabled/disabled state of this control //----------------------------------------------------------------------------- bool CPUTDropdown::IsEnabled() { if(CPUT_CONTROL_INACTIVE == mControlState) { return false; } return true; } // //----------------------------------------------------------------------------- bool CPUTDropdown::ContainsPoint(int x, int y) { // clicking the readout box CPUT_RECT inner, outer; CalculateReadoutRect(inner, outer); if( (x>outer.x) && (xouter.y) && (y mButtonRect.x+mButtonRect.width) || (y< mButtonRect.y) || (y> mButtonRect.y+mButtonRect.height) ) { return false; } return true; } // this is dimensions of top selection + tray // tray = open if(CPUT_DROPDOWN_GUI_MOUSE_PRESSED == mControlGuiState ) //CPUT_CONTROL_PRESSED == mControlState) { // in button area? if( (x> mButtonRect.x) && (x< mButtonRect.x+mButtonRect.width) && (y> mButtonRect.y) && (y< mButtonRect.y+mButtonRect.height) ) { return true; } // in the tray area? CPUT_RECT inner, outer; CalculateTrayRect(inner, outer); if( (x>outer.x) && (xouter.y) && (youter.x) && (xouter.y) && (y mButtonRect.x) && (x< mButtonRect.x+mButtonRect.width) && (y> mButtonRect.y) && (y< mButtonRect.y+mButtonRect.height) ) { return true; } return false; } // //----------------------------------------------------------------------------- bool CPUTDropdown::ContainsPointTrayArea(int x, int y) { // this is dimensions of top selection + tray // tray = open if(CPUT_CONTROL_ACTIVE == mControlState) { if(CPUT_DROPDOWN_GUI_MOUSE_PRESSED == mControlGuiState) { // in the tray area? CPUT_RECT inner, outer; CalculateTrayRect(inner, outer); if( (x>outer.x) && (xouter.y-1) && (ySetPosition(a,b); } Recalculate(); } // //----------------------------------------------------------------------------- void CPUTDropdown::GetDimensions(int &width, int &height) { CPUT_RECT inner, outer; CalculateReadoutRect(inner, outer); width = outer.width; height = outer.height; } // Recalculate all the quads used to draw this control //-------------------------------------------------------------------------------- void CPUTDropdown::Recalculate() { mpDropdownIdleSizeList; // calculate height/width of dropdown's interior based on string or button size CPUT_RECT inner, outer; CalculateReadoutRect(inner, outer); // calculate the pieces we'll need to rebuild int middleWidth = inner.width; int middleHeight = inner.height; // TODO: this should be calculated like const int mSmallestTopSizeIdle = mpDropdownIdleImageSizeList[0].height; const int mSmallestLeftSizeIdle =mpDropdownIdleImageSizeList[0].width; // clear and allocate the buffers const int VertexCount = 18*3*2; SAFE_DELETE_ARRAY(mpMirrorBufferActive); mpMirrorBufferActive = new CPUTGUIVertex[VertexCount]; SAFE_DELETE_ARRAY(mpMirrorBufferDisabled); mpMirrorBufferDisabled = new CPUTGUIVertex[VertexCount]; // // delete the old button quads for the middle sections //result = UnRegisterResizableInstanceQuads(); // Calculate the selected item area images // Idle button quads // left AddQuadIntoMirrorBuffer(mpMirrorBufferActive, 0*6, (float)mControlDimensions.x, (float)mControlDimensions.y, (float) mpDropdownIdleImageSizeList[0].width, (float) mpDropdownIdleImageSizeList[0].height, mpDropdownUVCoords_active[2*0], mpDropdownUVCoords_active[2*0+1] ); AddQuadIntoMirrorBuffer(mpMirrorBufferActive, 1*6, (float)mControlDimensions.x, (float)mControlDimensions.y+mSmallestTopSizeIdle, (float) mpDropdownIdleImageSizeList[1].width, (float) middleHeight, mpDropdownUVCoords_active[2*1], mpDropdownUVCoords_active[2*1+1] ); mpDropdownIdleSizeList[1].height = middleHeight; AddQuadIntoMirrorBuffer(mpMirrorBufferActive, 2*6, (float)mControlDimensions.x, (float)mControlDimensions.y+mSmallestTopSizeIdle+middleHeight, (float) mpDropdownIdleImageSizeList[2].width, (float) mpDropdownIdleImageSizeList[2].height, mpDropdownUVCoords_active[2*2], mpDropdownUVCoords_active[2*2+1] ); // middle AddQuadIntoMirrorBuffer(mpMirrorBufferActive, 3*6, (float)mControlDimensions.x+mSmallestLeftSizeIdle, (float)mControlDimensions.y, (float) middleWidth, (float) mpDropdownIdleImageSizeList[3].height, mpDropdownUVCoords_active[2*3], mpDropdownUVCoords_active[2*3+1] ); mpDropdownIdleSizeList[3].width = middleWidth; AddQuadIntoMirrorBuffer(mpMirrorBufferActive, 4*6, (float)mControlDimensions.x+mSmallestLeftSizeIdle, (float)mControlDimensions.y+mSmallestTopSizeIdle, (float) middleWidth, (float) middleHeight, mpDropdownUVCoords_active[2*4], mpDropdownUVCoords_active[2*4+1] ); mpDropdownIdleSizeList[4].width = middleWidth; mpDropdownIdleSizeList[4].height = middleHeight; AddQuadIntoMirrorBuffer(mpMirrorBufferActive, 5*6, (float)mControlDimensions.x+mSmallestLeftSizeIdle, (float)mControlDimensions.y+mSmallestTopSizeIdle+middleHeight, (float) middleWidth, (float) mpDropdownIdleImageSizeList[5].height, mpDropdownUVCoords_active[2*5], mpDropdownUVCoords_active[2*5+1] ); mpDropdownIdleSizeList[5].width = middleWidth; // right AddQuadIntoMirrorBuffer(mpMirrorBufferActive, 6*6, (float)mControlDimensions.x+mSmallestLeftSizeIdle+middleWidth, (float)mControlDimensions.y, (float) mpDropdownIdleImageSizeList[6].width, (float)mpDropdownIdleImageSizeList[6].height, mpDropdownUVCoords_active[2*6], mpDropdownUVCoords_active[2*6+1] ); AddQuadIntoMirrorBuffer(mpMirrorBufferActive, 7*6, (float)mControlDimensions.x+mSmallestLeftSizeIdle+middleWidth, (float)mControlDimensions.y+mSmallestTopSizeIdle, (float) mpDropdownIdleImageSizeList[7].width, (float)middleHeight, mpDropdownUVCoords_active[2*7], mpDropdownUVCoords_active[2*7+1] ); mpDropdownIdleSizeList[7].height = middleHeight; AddQuadIntoMirrorBuffer(mpMirrorBufferActive, 8*6, (float)mControlDimensions.x+mSmallestLeftSizeIdle+middleWidth, (float)mControlDimensions.y+mSmallestTopSizeIdle+middleHeight, (float) mpDropdownIdleImageSizeList[8].width, (float)mpDropdownIdleImageSizeList[8].height, mpDropdownUVCoords_active[2*8], mpDropdownUVCoords_active[2*8+1] ); // 2 buttons (up and pressed) at same location CPUT_RECT rect; CalculateButtonRect(rect); AddQuadIntoMirrorBuffer(mpMirrorBufferActive, 9*6, (float)rect.x, (float)rect.y, (float) mpDropdownIdleImageSizeList[ 9].width, (float)mpDropdownIdleImageSizeList[ 9].height, mpDropdownUVCoords_active[2*9], mpDropdownUVCoords_active[2*9+1] ); AddQuadIntoMirrorBuffer(mpMirrorBufferActive, 10*6, (float)rect.x, (float)rect.y, (float) mpDropdownIdleImageSizeList[10].width, (float)mpDropdownIdleImageSizeList[10].height, mpDropdownUVCoords_active[2*10], mpDropdownUVCoords_active[2*10+1] ); // if the dropdown tray is active, draw it int index=11; if(CPUT_CONTROL_ACTIVE == mControlState) { if((CPUT_DROPDOWN_GUI_MOUSE_PRESSED == mControlGuiState) && (0 != mpListOfSelectableItems.size())) { CalculateTrayRect(inner, outer); mTrayDimensions.width = inner.width; mTrayDimensions.height = inner.height; float tx, ty; tx = (float)outer.x; ty = (float)outer.y; CalculateMaxItemSize(mTrayDimensions.width, mTrayDimensions.height); mTrayDimensions.width += 2*CPUT_DROPDOWN_TEXT_PADDING; mTrayDimensions.height = inner.height; // lm AddQuadIntoMirrorBuffer(mpMirrorBufferActive, index*6, (float)tx, (float)ty, (float) mpDropdownIdleImageSizeList[CTrayLM].width, (float)mTrayDimensions.height, mpDropdownUVCoords_active[2*CTrayLM], mpDropdownUVCoords_active[2*CTrayLM+1] ); index++; // lb ty+=mTrayDimensions.height; // inner.height AddQuadIntoMirrorBuffer(mpMirrorBufferActive, index*6, (float)tx, (float)ty, (float) mpDropdownIdleImageSizeList[CTrayLB].width, (float)mpDropdownIdleImageSizeList[CTrayLB].height, mpDropdownUVCoords_active[2*CTrayLB], mpDropdownUVCoords_active[2*CTrayLB+1] ); index++; // mm tx+=mpDropdownIdleImageSizeList[CLeftMid].width; ty = (float)outer.y; AddQuadIntoMirrorBuffer(mpMirrorBufferActive, index*6, (float)tx, (float)ty, (float) mTrayDimensions.width, (float)mTrayDimensions.height, mpDropdownUVCoords_active[2*CTrayMM], mpDropdownUVCoords_active[2*CTrayMM+1] ); index++; // mb ty+=mTrayDimensions.height; AddQuadIntoMirrorBuffer(mpMirrorBufferActive, index*6, (float)tx, (float)ty, (float) mTrayDimensions.width, (float)mpDropdownIdleImageSizeList[CTrayMB].height, mpDropdownUVCoords_active[2*CTrayMB], mpDropdownUVCoords_active[2*CTrayMB+1] ); index++; // rm tx+=mTrayDimensions.width; ty = (float)outer.y; AddQuadIntoMirrorBuffer(mpMirrorBufferActive, index*6, (float)tx, (float)ty, (float) mpDropdownIdleImageSizeList[CTrayRM].width, (float)mTrayDimensions.height, mpDropdownUVCoords_active[2*CTrayRM], mpDropdownUVCoords_active[2*CTrayRM+1] ); index++; // rb ty += mTrayDimensions.height; AddQuadIntoMirrorBuffer(mpMirrorBufferActive, index*6, (float)tx, (float)ty, (float) mpDropdownIdleImageSizeList[CTrayRB].width, (float)mpDropdownIdleImageSizeList[CTrayRB].height, mpDropdownUVCoords_active[2*CTrayRB], mpDropdownUVCoords_active[2*CTrayRB+1] ); index++; // tray highlight int MaxSizeItemWidth, MaxSizeItemHeight; CalculateMaxItemSize(MaxSizeItemWidth, MaxSizeItemHeight); int sx = (inner.x + mpDropdownIdleImageSizeList[CLeftMid].width); int sy = (inner.y + CPUT_DROPDOWN_TEXT_PADDING); for(UINT i=0;iGetDimensions(rect.width, rect.height); sy+=rect.height + 2*CPUT_DROPDOWN_TEXT_PADDING; } } } // calculate the disabled item list // left AddQuadIntoMirrorBuffer(mpMirrorBufferDisabled, 0*6, (float)mControlDimensions.x, (float)mControlDimensions.y, (float) mpDropdownIdleImageSizeList[0].width, (float) mpDropdownIdleImageSizeList[0].height, mpDropdownUVCoords_disabled[2*0], mpDropdownUVCoords_disabled[2*0+1] ); AddQuadIntoMirrorBuffer(mpMirrorBufferDisabled, 1*6, (float)mControlDimensions.x, (float)mControlDimensions.y+mSmallestTopSizeIdle, (float) mpDropdownIdleImageSizeList[1].width, (float) middleHeight, mpDropdownUVCoords_disabled[2*1], mpDropdownUVCoords_disabled[2*1+1] ); mpDropdownIdleSizeList[1].height = middleHeight; AddQuadIntoMirrorBuffer(mpMirrorBufferDisabled, 2*6, (float)mControlDimensions.x, (float)mControlDimensions.y+mSmallestTopSizeIdle+middleHeight, (float) mpDropdownIdleImageSizeList[2].width, (float) mpDropdownIdleImageSizeList[2].height, mpDropdownUVCoords_disabled[2*2], mpDropdownUVCoords_disabled[2*2+1] ); // middle AddQuadIntoMirrorBuffer(mpMirrorBufferDisabled, 3*6, (float)mControlDimensions.x+mSmallestLeftSizeIdle, (float)mControlDimensions.y, (float) middleWidth, (float) mpDropdownIdleImageSizeList[3].height, mpDropdownUVCoords_disabled[2*3], mpDropdownUVCoords_disabled[2*3+1] ); mpDropdownIdleSizeList[3].width = middleWidth; AddQuadIntoMirrorBuffer(mpMirrorBufferDisabled, 4*6, (float)mControlDimensions.x+mSmallestLeftSizeIdle, (float)mControlDimensions.y+mSmallestTopSizeIdle, (float) middleWidth, (float) middleHeight, mpDropdownUVCoords_disabled[2*4], mpDropdownUVCoords_disabled[2*4+1] ); mpDropdownIdleSizeList[4].width = middleWidth; mpDropdownIdleSizeList[4].height = middleHeight; AddQuadIntoMirrorBuffer(mpMirrorBufferDisabled, 5*6, (float)mControlDimensions.x+mSmallestLeftSizeIdle, (float)mControlDimensions.y+mSmallestTopSizeIdle+middleHeight, (float) middleWidth, (float) mpDropdownIdleImageSizeList[5].height, mpDropdownUVCoords_disabled[2*5], mpDropdownUVCoords_disabled[2*5+1] ); mpDropdownIdleSizeList[5].width = middleWidth; // right AddQuadIntoMirrorBuffer(mpMirrorBufferDisabled, 6*6, (float)mControlDimensions.x+mSmallestLeftSizeIdle+middleWidth, (float)mControlDimensions.y, (float) mpDropdownIdleImageSizeList[6].width, (float)mpDropdownIdleImageSizeList[6].height, mpDropdownUVCoords_disabled[2*6], mpDropdownUVCoords_disabled[2*6+1] ); AddQuadIntoMirrorBuffer(mpMirrorBufferDisabled, 7*6, (float)mControlDimensions.x+mSmallestLeftSizeIdle+middleWidth, (float)mControlDimensions.y+mSmallestTopSizeIdle, (float) mpDropdownIdleImageSizeList[7].width, (float)middleHeight, mpDropdownUVCoords_disabled[2*7], mpDropdownUVCoords_disabled[2*7+1] ); mpDropdownIdleSizeList[7].height = middleHeight; AddQuadIntoMirrorBuffer(mpMirrorBufferDisabled, 8*6, (float)mControlDimensions.x+mSmallestLeftSizeIdle+middleWidth, (float)mControlDimensions.y+mSmallestTopSizeIdle+middleHeight, (float) mpDropdownIdleImageSizeList[8].width, (float)mpDropdownIdleImageSizeList[8].height, mpDropdownUVCoords_disabled[2*8], mpDropdownUVCoords_disabled[2*8+1] ); // 2 buttons (up and pressed) at same location CalculateButtonRect(rect); AddQuadIntoMirrorBuffer(mpMirrorBufferDisabled, 9*6, (float)rect.x, (float)rect.y, (float) mpDropdownIdleImageSizeList[ 9].width, (float)mpDropdownIdleImageSizeList[ 9].height, mpDropdownUVCoords_disabled[2*9], mpDropdownUVCoords_disabled[2*9+1] ); AddQuadIntoMirrorBuffer(mpMirrorBufferDisabled, 10*6, (float)rect.x, (float)rect.y, (float) mpDropdownIdleImageSizeList[10].width, (float)mpDropdownIdleImageSizeList[10].height, mpDropdownUVCoords_disabled[2*10], mpDropdownUVCoords_disabled[2*10+1] ); // -- now handle the text -- // draw the selected item in the readout display if(-1!=mSelectedItemIndex) { int x,y; CalculateReadoutTextPosition(x,y); cString string; mpListOfSelectableItems[mSelectedItemIndex]->GetString(string); mpSelectedItemCopy->SetText(string); mpSelectedItemCopy->SetPosition(x,y); mpListOfSelectableItems[mSelectedItemIndex]->SetPosition(x,y); } // calculate each of the individual dropdown text string locations CalculateTrayRect(inner, outer); float tx, ty; tx = (float)outer.x; ty = (float)outer.y; int sx = (inner.x + mpDropdownIdleImageSizeList[CLeftMid].width); int sy = (inner.y + CPUT_DROPDOWN_TEXT_PADDING); for(UINT i=0;iSetPosition(sx, sy); CPUT_RECT rect; mpListOfSelectableItems[i]->GetDimensions(rect.width, rect.height); sy+=rect.height + 2*CPUT_DROPDOWN_TEXT_PADDING; } // mark this as dirty mControlGraphicsDirty = true; } //-------------------------------------------------------------------------------- void CPUTDropdown::DrawIntoBuffer(CPUTGUIVertex *pVertexBufferMirror, UINT *pInsertIndex, UINT pMaxBufferSize, CPUTGUIVertex *pTextVertexBufferMirror, UINT *pTextInsertIndex, UINT MaxTextVertexBufferSize) { if(!mControlVisible) { return; } if((NULL==pVertexBufferMirror) || (NULL==pInsertIndex)) { return; } if(!mpMirrorBufferActive ) { return; } // Do we have enough room to put this control into the output buffer? int VertexCopyCount = GetOutputVertexCount(); ASSERT( (pMaxBufferSize >= *pInsertIndex + VertexCopyCount), _L("Too many CPUT GUI controls for allocated GUI buffer. Allocated GUI vertex buffer is too small.\n\nIncrease CPUT_GUI_BUFFER_SIZE size.") ); switch(mControlState) { case CPUT_CONTROL_ACTIVE: // copy the standard part of the control (selected box) - first 9 quads memcpy(&pVertexBufferMirror[*pInsertIndex], mpMirrorBufferActive, sizeof(CPUTGUIVertex)*6*9); *pInsertIndex+= 6*9; if((CPUT_DROPDOWN_GUI_MOUSE_PRESSED == mControlGuiState) )//&& (0 != mpListOfSelectableItems.size())) { // copy the 'down' button memcpy(&pVertexBufferMirror[*pInsertIndex], &mpMirrorBufferActive[10*6], sizeof(CPUTGUIVertex)*6*1); } else { // copy the 'up' button memcpy(&pVertexBufferMirror[*pInsertIndex], &mpMirrorBufferActive[9*6], sizeof(CPUTGUIVertex)*6*1); } *pInsertIndex+= 6*1; // tray is down, draw it, +1 for the highlit item if((CPUT_DROPDOWN_GUI_MOUSE_PRESSED == mControlGuiState) && (0 != mpListOfSelectableItems.size())) { int QuadsInTray = 6; memcpy(&pVertexBufferMirror[*pInsertIndex], &mpMirrorBufferActive[11*6], sizeof(CPUTGUIVertex)*6*(QuadsInTray+1)); *pInsertIndex+= 6*(QuadsInTray+1); } break; case CPUT_CONTROL_INACTIVE: // copy the inactive images into the stream memcpy(&pVertexBufferMirror[*pInsertIndex], mpMirrorBufferDisabled, sizeof(CPUTGUIVertex)*6*10); *pInsertIndex+= 6*10; break; default: // error! unknown state ASSERT(0,_L("CPUTButton: Control is in unknown state")); return; } // -- draw the text -- // draw selected item in the selection list if(NULL!=mpSelectedItemCopy) { mpSelectedItemCopy->DrawIntoBuffer((CPUTGUIVertex*)pTextVertexBufferMirror, pTextInsertIndex, MaxTextVertexBufferSize); } // draw the tray items if((CPUT_DROPDOWN_GUI_MOUSE_PRESSED == mControlGuiState) && (0 != mpListOfSelectableItems.size())) { for(UINT i=0;iDrawIntoBuffer((CPUTGUIVertex*)pTextVertexBufferMirror, pTextInsertIndex, MaxTextVertexBufferSize); } } // we'll mark the control as no longer being 'dirty' mControlGraphicsDirty = false; } // This generates a quad with the supplied coordinates/uv's/etc. //-------------------------------------------------------------------------------- void CPUTDropdown::AddQuadIntoMirrorBuffer(CPUTGUIVertex *pMirrorBuffer, int index, float x, float y, float w, float h, float3 uv1, float3 uv2 ) { CPUTColor4 color; color.r = 1.0f;color.g = 1.0f;color.b = 1.0f;color.a = 1.0f; pMirrorBuffer[index+0].Pos = float3( x + 0.0f, y + 0.0f, 1.0f); pMirrorBuffer[index+0].UV = float2(uv1.x, uv1.y); pMirrorBuffer[index+0].Color = color; pMirrorBuffer[index+1].Pos = float3( x + w, y + 0.0f, 1.0f); pMirrorBuffer[index+1].UV = float2(uv2.x, uv1.y); pMirrorBuffer[index+1].Color = color; pMirrorBuffer[index+2].Pos = float3( x + 0.0f, y + h, 1.0f); pMirrorBuffer[index+2].UV = float2(uv1.x, uv2.y); pMirrorBuffer[index+2].Color = color; pMirrorBuffer[index+3].Pos = float3( x + w, y + 0.0f, 1.0f); pMirrorBuffer[index+3].UV = float2(uv2.x, uv1.y); pMirrorBuffer[index+3].Color = color; pMirrorBuffer[index+4].Pos = float3( x + w, y + h, 1.0f); pMirrorBuffer[index+4].UV = float2(uv2.x, uv2.y); pMirrorBuffer[index+4].Color = color; pMirrorBuffer[index+5].Pos = float3( x + 0.0f, y +h, 1.0f); pMirrorBuffer[index+5].UV = float2(uv1.x, uv2.y); pMirrorBuffer[index+5].Color = color; } ================================================ FILE: CPUT/CPUT/CPUTDropdown.h ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #ifndef __CPUTDROPDOWN_H__ #define __CPUTDROPDOWN_H__ #include "CPUTControl.h" #include typedef enum CPUTDropdownGUIState { CPUT_DROPDOWN_GUI_MOUSE_NEUTRAL, CPUT_DROPDOWN_GUI_MOUSE_PRESSED, } CPUTDropdownGUIState; // forward declarations class CPUTTextureDX11; struct CPUTGUIVertex; class CPUTFont; class CPUTText; #define CPUT_NUM_IMAGES_IN_DROPDOWN_ARRAY 12 #define CPUT_NUM_QUADS_IN_DROPDOWN_ARRAY 18 #define CPUT_NUM_QUADS_IN_CLOSED_DROPDOWN 9 #define CPUT_DROPDOWN_TEXT_PADDING 2 const int CLeftTop = 0; const int CLeftMid = 1; const int CLeftBot = 2; const int CMidTop = 3; const int CMidMid = 4; const int CMidBot = 5; const int CRightTop = 6; const int CRightMid = 7; const int CRightBot = 8; const int CButtonUp = 9; const int CButtonDown = 10; #define CPUT_NUM_IMAGES_IN_DROPDOWN 12 // Dropdown base - common functionality for all dropdown controls //----------------------------------------------------------------------------- class CPUTDropdown:public CPUTControl { public: // button should self-register with the GuiController on create CPUTDropdown(CPUTDropdown& copy); CPUTDropdown(const cString ControlName, CPUTControlID id, CPUTFont *pFont); virtual ~CPUTDropdown(); // CPUTControl virtual void GetPosition(int &x, int &y); virtual unsigned int GetOutputVertexCount(); //CPUTEventHandler virtual CPUTEventHandledCode HandleKeyboardEvent(CPUTKey key){UNREFERENCED_PARAMETER(key);return CPUT_EVENT_UNHANDLED;} //CPUTEventHandler virtual CPUTEventHandledCode HandleMouseEvent(int x, int y, int wheel, CPUTMouseState state); //CPUTControl virtual void SetEnable(bool in_bEnabled); virtual bool IsEnabled(); virtual bool ContainsPoint(int x, int y); virtual bool ContainsPointReadoutArea(int x, int y); virtual bool ContainsPointTrayArea(int x, int y); virtual void SetText(cString ControlText) {;} // doesn't apply to this control virtual void GetText(cString &ControlString) {UNREFERENCED_PARAMETER(ControlString);} // doesn't apply to this control virtual void GetDimensions(int &width, int &height); virtual void SetPosition(int x, int y); //CPUTDropdownDX11 void NumberOfSelectableItems(UINT &count); void GetSelectedItem(UINT &index); void GetSelectedItem(cString &Item); void SetSelectedItem(const UINT index); CPUTResult AddSelectionItem(const cString Item, bool IsSelected=false); void DeleteSelectionItem(const UINT index); void DeleteSelectionItem(const cString string); // Draw void DrawIntoBuffer(CPUTGUIVertex *pVertexBufferMirror, UINT *pInsertIndex, UINT pMaxBufferSize, CPUTGUIVertex *pTextVertexBufferMirror, UINT *pTextInsertIndex, UINT MaxTextVertexBufferSize); // Register assets static CPUTResult RegisterStaticResources(); static CPUTResult UnRegisterStaticResources(); CPUTResult RegisterInstanceResources(); CPUTResult UnRegisterInstanceResources(); protected: CPUT_RECT mControlDimensions; CPUTGUIControlState mControlState; CPUTDropdownGUIState mControlGuiState; void InitialStateSet(); // uber-buffer adds CPUTFont *mpFont; CPUTText *mpButtonText; static CPUT_SIZE mpDropdownIdleImageSizeList[CPUT_NUM_IMAGES_IN_DROPDOWN]; static CPUT_SIZE mpDropdownDisabledSizeList[CPUT_NUM_IMAGES_IN_DROPDOWN]; // uber-buffer per-instance CPUT_SIZE mpDropdownIdleSizeList[CPUT_NUM_IMAGES_IN_DROPDOWN]; CPUT_SIZE mpDropdownDisabledList[CPUT_NUM_IMAGES_IN_DROPDOWN]; CPUTGUIVertex *mpMirrorBufferActive; CPUTGUIVertex *mpMirrorBufferDisabled; void Recalculate(); void AddQuadIntoMirrorBuffer(CPUTGUIVertex *pMirrorBuffer, int index, float x, float y, float w, float h, float3 uv1, float3 uv2 ); // instance data CPUT_RECT mButtonRect; CPUT_RECT mReadoutRectInside; CPUT_RECT mReadoutRectOutside; CPUT_RECT mTrayDimensions; bool mbMouseInside; UINT mRevertItem; bool mbStartedClickInside; bool mbStartedClickInsideTray; // list of items in the dropdown UINT mSelectedItemIndex; //zero-based index that tells what item is currently highlighted (soft selected) UINT mConfirmedSelectedItemIndex; //zero-based index that tells what item was last confirm-selected (hard selected) std::vector mpListOfSelectableItems; CPUTText *mpSelectedItemCopy; bool mbSizeDirty; UINT mVertexStride; // stride and offsets of the image quads we're drawing on UINT mVertexOffset; // helper functions void CalculateButtonRect(CPUT_RECT& button); void CalculateReadoutRect(CPUT_RECT& inner, CPUT_RECT& outer); void CalculateTrayRect(CPUT_RECT& inner, CPUT_RECT& outer); void CalculateReadoutTextPosition(int &x, int &y); void CalculateMaxItemSize(int &width, int &height); // sets which item is to be highlighted on a dropdown list void SetHighlightedItem(const UINT index); }; #endif //#ifndef __CPUTDROPDOWN_H__ ================================================ FILE: CPUT/CPUT/CPUTEventHandler.h ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #ifndef __CPUTEVENTHANDLER_H__ #define __CPUTEVENTHANDLER_H__ #include // event handling enums //----------------------------------------------------------------------------- enum CPUTKey { KEY_NONE, // caps keys KEY_A, KEY_B, KEY_C, KEY_D, KEY_E, KEY_F, KEY_G, KEY_H, KEY_I, KEY_J, KEY_K, KEY_L, KEY_M, KEY_N, KEY_O, KEY_P, KEY_Q, KEY_R, KEY_S, KEY_T, KEY_U, KEY_V, KEY_W, KEY_X, KEY_Y, KEY_Z, // number keys KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9, KEY_0, // symbols KEY_SPACE, KEY_BACKQUOTE, KEY_TILDE, KEY_EXCLAMATION, KEY_AT, KEY_HASH, KEY_$, KEY_PERCENT, KEY_CARROT, KEY_ANDSIGN, KEY_STAR, KEY_OPENPAREN, KEY_CLOSEPARN, KEY__, KEY_MINUS, KEY_PLUS, KEY_OPENBRACKET, KEY_CLOSEBRACKET, KEY_OPENBRACE, KEY_CLOSEBRACE, KEY_BACKSLASH, KEY_PIPE, KEY_SEMICOLON, KEY_COLON, KEY_SINGLEQUOTE, KEY_QUOTE, KEY_COMMA, KEY_PERIOD, KEY_SLASH, KEY_LESS, KEY_GREATER, KEY_QUESTION, // function keys KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_F12, // special keys KEY_HOME, KEY_END, KEY_INSERT, KEY_DELETE, KEY_PAGEUP, KEY_PAGEDOWN, KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, KEY_BACKSPACE, KEY_ENTER, KEY_TAB, KEY_PAUSE, KEY_CAPSLOCK, KEY_ESCAPE, // control keys KEY_LEFT_SHIFT, KEY_RIGHT_SHIFT, KEY_LEFT_CTRL, KEY_RIGHT_CTRL, KEY_LEFT_ALT, KEY_RIGHT_ALT, }; // these must be unique because we bitwise && them to get multiple states enum CPUTMouseState { CPUT_MOUSE_NONE = 0, CPUT_MOUSE_LEFT_DOWN = 1, CPUT_MOUSE_RIGHT_DOWN = 2, CPUT_MOUSE_MIDDLE_DOWN = 4, CPUT_MOUSE_CTRL_DOWN = 8, CPUT_MOUSE_SHIFT_DOWN = 16, CPUT_MOUSE_WHEEL = 32, }; enum CPUTEventHandledCode { CPUT_EVENT_HANDLED = 0, CPUT_EVENT_UNHANDLED = 1, CPUT_EVENT_PASSTHROUGH = 2, }; // Event handler interface - used by numerous classes in the system class CPUTEventHandler { public: virtual CPUTEventHandledCode HandleKeyboardEvent(CPUTKey key)=0; virtual CPUTEventHandledCode HandleMouseEvent(int x, int y, int wheel, CPUTMouseState state)=0; }; #endif //#ifndef __CPUTEVENTHANDLER_H__ ================================================ FILE: CPUT/CPUT/CPUTFont.cpp ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #include "CPUTFont.h" #ifdef CPUT_FOR_DX11 #include "CPUTFontDX11.h" #else #error You must supply a target graphics API (ex: #define CPUT_FOR_DX11), or implement the target API for this file. #endif int gFontMap_active[] = { 'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z', // lower 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z', // upper '1','2','3','4','5','6','7','8','9','0', // numbers ',', '.','/',';','\'','[',']','\\','`','~','!','@','#','$','%','^','&','*','(',')','_','+','{','}','|',':','\"','<','>','?','-','=', // symbols ' ','\t', // space, tab -1 }; //----------------------------------------------------------------------------- CPUTFont *CPUTFont::CreateFont( cString FontName, cString AbsolutePathAndFilename ) { // TODO: accept DX11/OGL param to control which platform we generate. // TODO: be sure to support the case where we want to support only one of them #ifdef CPUT_FOR_DX11 return CPUTFontDX11::CreateFont( FontName, AbsolutePathAndFilename ); #else #error You must supply a target graphics API (ex: #define CPUT_FOR_DX11), or implement the target API for this file. #endif } // return the size in pixels of the glyph //----------------------------------------------------------------------------- CPUT_SIZE CPUTFont::GetGlyphSize(const char c) { CPUT_SIZE size; size.height=0; size.width=0; int index = FindGlyphIndex(c); if(-1!=index) { size.width=mpGlyphSizes[index].width; size.height=mpGlyphSizes[index].height; } return size; } // return the uv coordinates for the given glyph // upper left/lower right //----------------------------------------------------------------------------- void CPUTFont::GetGlyphUVS(const char c, const bool bEnabledVersion, float3 &UV1, float3 &UV2) { int index = FindGlyphIndex(c); if(-1!=index) { if(bEnabledVersion) { UV1.x=mpGlyphUVCoords[4*index+0]; UV1.y=mpGlyphUVCoords[4*index+1]; UV2.x=mpGlyphUVCoords[4*index+2]; UV2.y=mpGlyphUVCoords[4*index+3]; } else { UV1.x=mpGlyphUVCoordsDisabled[4*index+0]; UV1.y=mpGlyphUVCoordsDisabled[4*index+1]; UV2.x=mpGlyphUVCoordsDisabled[4*index+2]; UV2.y=mpGlyphUVCoordsDisabled[4*index+3]; } } } // find the index of the glyph that corresponds to the char passed in //----------------------------------------------------------------------------- int CPUTFont::FindGlyphIndex(const char c) { int index=0; while(-1 != gFontMap_active[index]) { if(c == gFontMap_active[index]) { return index; } index++; } // not found return -1; } ================================================ FILE: CPUT/CPUT/CPUTFont.h ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #ifndef __CPUTFONT_H__ #define __CPUTFONT_H__ #include "CPUT.h" #include "CPUTRefCount.h" #define CPUT_MAX_NUMBER_OF_CHARACTERS 256 class CPUTFont : public CPUTRefCount { friend class CPUTFontDX11; public: static CPUTFont *CreateFont( cString FontName, cString AbsolutePathAndFilename); CPUT_SIZE GetGlyphSize(const char c); void GetGlyphUVS(const char c, const bool bEnabledVersion, float3& UV1, float3& UV2); protected: ~CPUTFont(){} // Destructor is not public. Must release instead of delete. // atlas texture info float mAtlasWidth; float mAtlasHeight; float mDisabledYOffset; UINT mNumberOfGlyphsInAtlas; int mpGlyphMap[CPUT_MAX_NUMBER_OF_CHARACTERS]; int mpGlyphStarts[CPUT_MAX_NUMBER_OF_CHARACTERS]; CPUT_SIZE mpGlyphSizes[CPUT_MAX_NUMBER_OF_CHARACTERS]; float mpGlyphUVCoords[4*CPUT_MAX_NUMBER_OF_CHARACTERS]; // 4 floats/glyph = upper-left:(uv1.x, uv1.y), lower-right:(uv2.x, uv2.y) float mpGlyphUVCoordsDisabled[4*CPUT_MAX_NUMBER_OF_CHARACTERS]; // 4 floats/glyph = upper-left:(uv1.x, uv1.y), lower-right:(uv2.x, uv2.y) // helper functions int FindGlyphIndex(const char c); }; #endif // #ifndef __CPUTFONT_H__ ================================================ FILE: CPUT/CPUT/CPUTFontDX11.cpp ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #include "CPUTFontDX11.h" #include "CPUTTextureDX11.h" int gFontStartLocations_active[] = { // auto-generated by off-line tool 0,7,15,22,30,37,42,50,58,61,66,73,77, //a-m 88,96,104,111,119,124,132,137,144,152,162,170,178, //n-z 185,195,204,213,223,231,239,249,258,262,270,279,287, //A-M 297,306,317,325,335,344,352,360,369,379,392,401,410, //N-Z 419,425,432,440,448,456,463,470,478,485, //1-0 493,497,500,506,510,515,520,525,530,535,543,547,561, //symbols ,-# 569,577,589,596,606,612,616,621,629,637,642,648,653, //symbols $-: 657,664,672,680,688,694,701 //symbols "-= ,705,755, //dead spot used for the space char and tab -1, //end marker }; // height of the font const int gFontHeight = 12; // y offset to get to the next font down (the disabled font) const int gYOffset = 12; //----------------------------------------------------------------------------- CPUTFont *CPUTFontDX11::CreateFont( cString FontName, cString AbsolutePathAndFilename ) { CPUTFontDX11 *pNewFont = new CPUTFontDX11(); // load the actual font image CPUTAssetLibraryDX11 *pAssetLibrary = (CPUTAssetLibraryDX11*)CPUTAssetLibraryDX11::GetAssetLibrary(); pNewFont->mpTextAtlas= (CPUTTextureDX11*) pAssetLibrary->GetTexture(AbsolutePathAndFilename, true); ASSERT(pNewFont->mpTextAtlas, _L("CPUTFontDX11::CreateFont() - Error loading specified font texture")); // get the TextureResourceView pNewFont->mpTextAtlasView = pNewFont->mpTextAtlas->GetShaderResourceView(); pNewFont->mpTextAtlasView->AddRef(); // CPUTSetDebugName( pNewFont->mpTextAtlasView, _L("GUI TextAtlasView")); // Get and store the atlas size D3D11_TEXTURE2D_DESC TextureDesc; ID3D11Texture2D *p2DTexture = NULL; // get the ID3D11Resource from the ID3D11ShaderResourceView ID3D11Resource *pResource = NULL; pNewFont->mpTextAtlasView->GetResource(&pResource); // get the ID3D11Texture2D from the ID3D11Resource HRESULT hr = pResource->QueryInterface(__uuidof(ID3D11Texture2D) ,(void**)&p2DTexture); ASSERT( SUCCEEDED(hr), _L("CPUTFontDX11::CreateFont() - Error loading specified font texture")); p2DTexture->GetDesc(&TextureDesc); // store the image dimensions/size pNewFont->mAtlasWidth = (float)TextureDesc.Width; pNewFont->mAtlasHeight = (float)TextureDesc.Height; // release the pointers SAFE_RELEASE(pResource); SAFE_RELEASE(p2DTexture); // todo: can use the tick marks above each glyph to determine start/stop // But this requires me to be able to map the texture so I can walk it and find the glyph start/stop // points. Currently we register all textures as immutable, so we can't map the texture. // For now, I have an offline tool generate the mappings found in gFontStartLocations_active[] int index=0; while(-1 != gFontStartLocations_active[index]) { // record the start location pNewFont->mpGlyphStarts[index] = gFontStartLocations_active[index]; // calculate the size of each glyph in pixels pNewFont->mpGlyphSizes[index].width = gFontStartLocations_active[index+1] - gFontStartLocations_active[index]; pNewFont->mpGlyphSizes[index].height = gFontHeight-1; // -1 for top line of glyph start marker dots // calculate the UV coordinates for the 'enabled' version of this glyph pNewFont->mpGlyphUVCoords[4*index+0] = gFontStartLocations_active[index]/pNewFont->mAtlasWidth; // u1 - upper left x pNewFont->mpGlyphUVCoords[4*index+1] = 1.0f/pNewFont->mAtlasHeight; // v1 - upper left y pNewFont->mpGlyphUVCoords[4*index+2] = gFontStartLocations_active[index+1]/pNewFont->mAtlasWidth; // u2 - lower right x pNewFont->mpGlyphUVCoords[4*index+3] = gFontHeight/pNewFont->mAtlasHeight; // v2 - lower right y // calculate the UV coordinates of the 'disabled'/greyed version of this glyph pNewFont->mpGlyphUVCoordsDisabled[4*index+0] = gFontStartLocations_active[index]/pNewFont->mAtlasWidth; // u1 - upper left x pNewFont->mpGlyphUVCoordsDisabled[4*index+1] = (gYOffset+1)/pNewFont->mAtlasHeight; // v1 - upper left y pNewFont->mpGlyphUVCoordsDisabled[4*index+2] = gFontStartLocations_active[index+1]/pNewFont->mAtlasWidth; // u2 - lower right x pNewFont->mpGlyphUVCoordsDisabled[4*index+3] = (gFontHeight+gYOffset)/pNewFont->mAtlasHeight; // v2 - lower right y index++; } pNewFont->mNumberOfGlyphsInAtlas = index; // add font to the asset library CPUTAssetLibrary::GetAssetLibrary()->AddFont( FontName, pNewFont); return pNewFont; } // Constructor //----------------------------------------------------------------------------- CPUTFontDX11::CPUTFontDX11():mpTextAtlas(NULL), mpTextAtlasView(NULL) { mDisabledYOffset = 0; } // Destructor //----------------------------------------------------------------------------- CPUTFontDX11::~CPUTFontDX11() { // release the texture atlas SAFE_RELEASE(mpTextAtlasView); SAFE_RELEASE(mpTextAtlas); } // Return the texture atlas texture //----------------------------------------------------------------------------- CPUTTextureDX11* CPUTFontDX11::GetAtlasTexture() { return mpTextAtlas; } // Return the texture atlas texture resource view //----------------------------------------------------------------------------- ID3D11ShaderResourceView* CPUTFontDX11::GetAtlasTextureResourceView() { return mpTextAtlasView; } // Load glyph mapping file // The map file in an ordered list that tells you which glyph in the texture // corresponds to the ASCII character value in this file (char->image lookup) //-------------------------------------------------------------------------------- CPUTResult CPUTFontDX11::LoadGlyphMappingFile(const cString fileName) { return CPUT_SUCCESS; } ================================================ FILE: CPUT/CPUT/CPUTFontDX11.h ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #ifndef __CPUTFONTDX11_H__ #define __CPUTFONTDX11_H__ #include "CPUT.h" #include "CPUTRefCount.h" #include "CPUTFont.h" class CPUTTextureDX11; class CPUTFontDX11 : public CPUTFont { protected: ~CPUTFontDX11(); // Destructor is not public. Must release instead of delete. public: CPUTFontDX11(); static CPUTFont *CreateFont( cString FontName, cString AbsolutePathAndFilename); CPUTTextureDX11 *GetAtlasTexture(); ID3D11ShaderResourceView *GetAtlasTextureResourceView(); private: CPUTTextureDX11 *mpTextAtlas; ID3D11ShaderResourceView *mpTextAtlasView; CPUTResult LoadGlyphMappingFile(const cString fileName); }; #endif // #ifndef __CPUTFONTDX11_H__ ================================================ FILE: CPUT/CPUT/CPUTFrustum.cpp ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #include "CPUTFrustum.h" #include "CPUTCamera.h" //----------------------------------------------- void CPUTFrustum::InitializeFrustum( CPUTCamera *pCamera ) { InitializeFrustum( pCamera->GetNearPlaneDistance(), pCamera->GetFarPlaneDistance(), pCamera->GetAspectRatio(), pCamera->GetFov(), pCamera->GetPosition(), pCamera->GetLook(), pCamera->GetUp() ); } //----------------------------------------------- void CPUTFrustum::InitializeFrustum ( float nearClipDistance, float farClipDistance, float aspectRatio, float fov, const float3 &position, const float3 &look, const float3 &up ) { // ****************************** // This function computes the position of each of the frustum's eight points. // It also computes the normal of each of the frustum's six planes. // ****************************** mNumFrustumVisibleModels = 0; mNumFrustumCulledModels = 0; // We have the camera's up and look, but we also need right. float3 right = cross3( up, look ); // Compute the position of the center of the near and far clip planes. float3 nearCenter = position + look * nearClipDistance; float3 farCenter = position + look * farClipDistance; // Compute the width and height of the near and far clip planes float tanHalfFov = tanf(0.5f*fov); float halfNearHeight = nearClipDistance * tanHalfFov; float halfNearWidth = halfNearHeight * aspectRatio; float halfFarHeight = farClipDistance * tanHalfFov; float halfFarWidth = halfFarHeight * aspectRatio; // Create two vectors each for the near and far clip planes. // These are the scaled up and right vectors. float3 upNear = up * halfNearHeight; float3 rightNear = right * halfNearWidth; float3 upFar = up * halfFarHeight; float3 rightFar = right * halfFarWidth; // Use the center positions and the up and right vectors // to compute the positions for the near and far clip plane vertices (four each) mpPosition[0] = nearCenter + upNear - rightNear; // near top left mpPosition[1] = nearCenter + upNear + rightNear; // near top right mpPosition[2] = nearCenter - upNear + rightNear; // near bottom right mpPosition[3] = nearCenter - upNear - rightNear; // near bottom left mpPosition[4] = farCenter + upFar - rightFar; // far top left mpPosition[5] = farCenter + upFar + rightFar; // far top right mpPosition[6] = farCenter - upFar + rightFar; // far bottom right mpPosition[7] = farCenter - upFar - rightFar; // far bottom left // Compute some of the frustum's edge vectors. We will cross these // to get the normals for each of the six planes. float3 nearTop = mpPosition[1] - mpPosition[0]; float3 nearLeft = mpPosition[3] - mpPosition[0]; float3 topLeft = mpPosition[4] - mpPosition[0]; float3 bottomRight = mpPosition[2] - mpPosition[6]; float3 farRight = mpPosition[5] - mpPosition[6]; float3 farBottom = mpPosition[7] - mpPosition[6]; mpNormal[0] = cross3(nearTop, nearLeft).normalize(); // near clip plane mpNormal[1] = cross3(nearLeft, topLeft).normalize(); // left mpNormal[2] = cross3(topLeft, nearTop).normalize(); // top mpNormal[3] = cross3(farBottom, bottomRight).normalize(); // bottom mpNormal[4] = cross3(bottomRight, farRight).normalize(); // right mpNormal[5] = cross3(farRight, farBottom).normalize(); // far clip plane } //----------------------------------------------- bool CPUTFrustum::IsVisible( const float3 ¢er, const float3 &half ){ // TODO: There are MUCH more efficient ways to do this. float3 pBoundingBoxPosition[8]; pBoundingBoxPosition[0] = center + float3( half.x, half.y, half.z ); pBoundingBoxPosition[1] = center + float3( half.x, half.y, -half.z ); pBoundingBoxPosition[2] = center + float3( half.x, -half.y, half.z ); pBoundingBoxPosition[3] = center + float3( half.x, -half.y, -half.z ); pBoundingBoxPosition[4] = center + float3( -half.x, half.y, half.z ); pBoundingBoxPosition[5] = center + float3( -half.x, half.y, -half.z ); pBoundingBoxPosition[6] = center + float3( -half.x, -half.y, half.z ); pBoundingBoxPosition[7] = center + float3( -half.x, -half.y, -half.z ); // Test each bounding box point against each of the six frustum planes. // Note: we need a point on the plane to compute the distance to the plane. // We only need two of our frustum's points to do this. A corner vertex is on // three of the six planes. We need two of these corners to have a point // on all six planes. UINT pPointIndex[6] = {0,0,0,6,6,6}; UINT ii; for( ii=0; ii<6; ii++ ) { bool allEightPointsOutsidePlane = true; float3 *pNormal = &mpNormal[ii]; float3 *pPlanePoint = &mpPosition[pPointIndex[ii]]; float3 planeToPoint; float distanceToPlane; UINT jj; for( jj=0; jj<8; jj++ ) { planeToPoint = pBoundingBoxPosition[jj] - *pPlanePoint; distanceToPlane = dot3( *pNormal, planeToPoint ); if( distanceToPlane < 0.0f ) { allEightPointsOutsidePlane = false; break; // from for. No point testing any more points against this plane. } } if( allEightPointsOutsidePlane ) { mNumFrustumCulledModels++; return false; } } // Tested all eight points against all six planes and none of the planes // had all eight points outside. mNumFrustumVisibleModels++; return true; } ================================================ FILE: CPUT/CPUT/CPUTFrustum.h ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #ifndef _CPUTFRUSTUM_H #define _CPUTFRUSTUM_H #include "CPUT.h" #include "CPUTMath.h" class CPUTCamera; class CPUTFrustum { public: float3 mpPosition[8]; float3 mpNormal[6]; UINT mNumFrustumVisibleModels; UINT mNumFrustumCulledModels; CPUTFrustum(){} ~CPUTFrustum(){} void InitializeFrustum ( float nearClipDistance, float farClipDistance, float aspectRatio, float fov, const float3 &position, const float3 &look, const float3 &up ); void InitializeFrustum( CPUTCamera *pCamera ); bool IsVisible( const float3 ¢er, const float3 &half ); }; #endif // _CPUTFRUSTUM_H ================================================ FILE: CPUT/CPUT/CPUTGeometryShaderDX11.cpp ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #include "CPUTGeometryShaderDX11.h" #include "CPUTAssetLibraryDX11.h" CPUTGeometryShaderDX11 *CPUTGeometryShaderDX11::CreateGeometryShader( const cString &name, ID3D11Device *pD3dDevice, const cString &shaderMain, const cString &shaderProfile ) { ID3DBlob *pCompiledBlob = NULL; ID3D11GeometryShader *pNewGeometryShader = NULL; CPUTAssetLibraryDX11 *pAssetLibrary = (CPUTAssetLibraryDX11*)CPUTAssetLibrary::GetAssetLibrary(); CPUTResult result = pAssetLibrary->CompileShaderFromFile(name, shaderMain, shaderProfile, &pCompiledBlob); ASSERT( CPUTSUCCESS(result), _L("Error compiling Geometry shader:\n\n") ); // Create the Geometry shader // TODO: Move to Geometry shader class HRESULT hr = pD3dDevice->CreateGeometryShader( pCompiledBlob->GetBufferPointer(), pCompiledBlob->GetBufferSize(), NULL, &pNewGeometryShader ); ASSERT( SUCCEEDED(hr), _L("Error creating Geometry shader:\n\n") ); // cString DebugName = _L("CPUTAssetLibraryDX11::GetGeometryShader ")+name; // CPUTSetDebugName(pNewGeometryShader, DebugName); CPUTGeometryShaderDX11 *pNewCPUTGeometryShader = new CPUTGeometryShaderDX11( pNewGeometryShader, pCompiledBlob ); // add shader to library pAssetLibrary->AddGeometryShader(name + shaderMain + shaderProfile, pNewCPUTGeometryShader); // pNewCPUTGeometryShader->Release(); // We've added it to the library, so release our reference // return the shader (and blob) return pNewCPUTGeometryShader; } //-------------------------------------------------------------------------------------- CPUTGeometryShaderDX11 *CPUTGeometryShaderDX11::CreateGeometryShaderFromMemory( const cString &name, ID3D11Device *pD3dDevice, const cString &shaderMain, const cString &shaderProfile, const char *pShaderSource ) { ID3DBlob *pCompiledBlob = NULL; ID3D11GeometryShader *pNewGeometryShader = NULL; CPUTAssetLibraryDX11 *pAssetLibrary = (CPUTAssetLibraryDX11*)CPUTAssetLibrary::GetAssetLibrary(); CPUTResult result = pAssetLibrary->CompileShaderFromMemory(pShaderSource, shaderMain, shaderProfile, &pCompiledBlob); ASSERT( CPUTSUCCESS(result), _L("Error creating Geometry shader:\n\n") ); // Create the Geometry shader // TODO: Move to Geometry shader class HRESULT hr = pD3dDevice->CreateGeometryShader( pCompiledBlob->GetBufferPointer(), pCompiledBlob->GetBufferSize(), NULL, &pNewGeometryShader ); ASSERT( SUCCEEDED(hr), _L("Error compiling Geometry shader:\n\n") ); // cString DebugName = _L("CPUTAssetLibraryDX11::GetGeometryShader ")+name; // CPUTSetDebugName(pNewGeometryShader, DebugName); CPUTGeometryShaderDX11 *pNewCPUTGeometryShader = new CPUTGeometryShaderDX11( pNewGeometryShader, pCompiledBlob ); // add shader to library pAssetLibrary->AddGeometryShader(name + shaderMain + shaderProfile, pNewCPUTGeometryShader); // pNewCPUTGeometryShader->Release(); // We've added it to the library, so release our reference // return the shader (and blob) return pNewCPUTGeometryShader; } ================================================ FILE: CPUT/CPUT/CPUTGeometryShaderDX11.h ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #ifndef _CPUTGEOMETRYSHADERDX11_H #define _CPUTGEOMETRYSHADERDX11_H #include "CPUT.h" #include "CPUTShaderDX11.h" class CPUTGeometryShaderDX11 : public CPUTShaderDX11 { protected: ID3D11GeometryShader *mpGeometryShader; // Destructor is not public. Must release instead of delete. ~CPUTGeometryShaderDX11(){ SAFE_RELEASE(mpGeometryShader); } public: static CPUTGeometryShaderDX11 *CreateGeometryShader( const cString &name, ID3D11Device *pD3dDevice, const cString &shaderMain, const cString &shaderProfile ); static CPUTGeometryShaderDX11 *CreateGeometryShaderFromMemory( const cString &name, ID3D11Device *pD3dDevice, const cString &shaderMain, const cString &shaderProfile, const char *pShaderSource ); CPUTGeometryShaderDX11() : mpGeometryShader(NULL), CPUTShaderDX11(NULL) {} CPUTGeometryShaderDX11(ID3D11GeometryShader *pD3D11GeometryShader, ID3DBlob *pBlob) : mpGeometryShader(pD3D11GeometryShader), CPUTShaderDX11(pBlob) {} ID3DBlob *GetBlob() { return mpBlob; } ID3D11GeometryShader *GetNativeGeometryShader() { return mpGeometryShader; } }; #endif //_CPUTGEOMETRYSHADER_H ================================================ FILE: CPUT/CPUT/CPUTGuiController.cpp ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #include "CPUTGuiController.h" // Constructor //----------------------------------------------------------------------------- CPUTGuiController::CPUTGuiController(): mControlPanelIDList(NULL), mActiveControlPanelSlotID(CPUT_CONTROL_ID_INVALID), mpHandler(NULL), mpFocusControl(NULL), mbAutoLayout(true), mResourceDirectory(_L("./")), mUberBufferDirty(true), mRecalculateLayout(false) { // clear all controls and panel lists DeleteAllControls(); } // Destructor //----------------------------------------------------------------------------- CPUTGuiController::~CPUTGuiController() { // cleanup DeleteAllControls(); } //CPUTEventHandler members // Handle mouse events // The gui controller dispatches the event to each control until handled or // it passes through unhandled //----------------------------------------------------------------------------- CPUTEventHandledCode CPUTGuiController::HandleMouseEvent(int x, int y, int wheel, CPUTMouseState state) { // if there is no active panel, then we just return if(CPUT_CONTROL_ID_INVALID == mActiveControlPanelSlotID) { return CPUT_EVENT_PASSTHROUGH; } // walk the list of controls on the screen and see if they are to handle any of these events CPUTEventHandledCode EventResult = CPUT_EVENT_PASSTHROUGH; for(UINT i=0; imControlList.size(); i++) { if(CPUT_EVENT_HANDLED == EventResult) { // walk the rest of the controls, updating them with the event, but with an 'invalid' location // this is a not-so-great but works bug fix for this problem: // If you click on another control besides a dropped-down dropdown, then you get a painting error // You need to send a closed event to any remaining dropdowns... mControlPanelIDList[mActiveControlPanelSlotID]->mControlList[i]->HandleMouseEvent(-1,-1,wheel,state); } else { EventResult = mControlPanelIDList[mActiveControlPanelSlotID]->mControlList[i]->HandleMouseEvent(x,y,wheel,state); // if the control says it handled this event, do not pass it through to underlying controls // this is important for things like dropdowns that could dynamically overlap other controls if( CPUT_EVENT_HANDLED == EventResult) { mpFocusControl = mControlPanelIDList[mActiveControlPanelSlotID]->mControlList[i]; //return EventResult; } } } return EventResult; //CPUT_EVENT_PASSTHROUGH; } // Turns on/off automatic arrangement of controls on right side of screen //----------------------------------------------------------------------------- void CPUTGuiController::EnableAutoLayout(bool UseAutoLayout) { mbAutoLayout = UseAutoLayout; } // sets the resource directory to use when loading GUI resources //----------------------------------------------------------------------------- CPUTResult CPUTGuiController::SetResourceDirectory(const cString ResourceDirectory) { // check to see if the specified directory is valid CPUTResult result = CPUT_SUCCESS; // resolve the directory to a full path cString FullPath; CPUTOSServices *pServices = CPUTOSServices::GetOSServices(); result = pServices->ResolveAbsolutePathAndFilename(ResourceDirectory, &FullPath); if(CPUTFAILED(result)) { return result; } // check existence of directory result = pServices->DoesDirectoryExist(FullPath); if(CPUTFAILED(result)) { return result; } // set the resource directory (absolute path) mResourceDirectory = FullPath; return result; } // gets the resource directory used when loading GUI resources //----------------------------------------------------------------------------- void CPUTGuiController::GetResourceDirectory(cString &ResourceDirectory) { ResourceDirectory = mResourceDirectory; } // Panels // finds panel with matching ID and sets it as the active one // if panelID is CPUT_CONTROL_ID_INVALID - it will disable all panels //----------------------------------------------------------------------------- CPUTResult CPUTGuiController::SetActivePanel(CPUTControlID panelID) { CPUTControlID panelSlotID = FindPanelIDIndex(panelID); // if we found it, set it active if(CPUT_CONTROL_ID_INVALID == panelSlotID) { return CPUT_ERROR_NOT_FOUND; } // store previously active control mControlPanelIDList[mActiveControlPanelSlotID]->mpFocusControl = mpFocusControl; // change the active panel and refresh screen mActiveControlPanelSlotID = panelSlotID; mpFocusControl = mControlPanelIDList[mActiveControlPanelSlotID]->mpFocusControl; // trigger refresh if(mbAutoLayout) { Resize(); } return CPUT_SUCCESS; } // returns the ID of the active panel //----------------------------------------------------------------------------- CPUTControlID CPUTGuiController::GetActivePanelID() { if(CPUT_CONTROL_ID_INVALID == mActiveControlPanelSlotID) return CPUT_CONTROL_ID_INVALID; return mControlPanelIDList[mActiveControlPanelSlotID]->mpanelID; } // Get the list of controls currently being displayed //----------------------------------------------------------------------------- CPUTResult CPUTGuiController::GetActiveControlList(std::vector *ppControlList) { ASSERT(ppControlList != NULL, _L("CPUTGuiController::GetActiveControlList - pControlList is NULL")); if(CPUT_CONTROL_ID_INVALID == mActiveControlPanelSlotID) { // return CPUT_GUI_INVALID_CONTROL_ID; } if(NULL==ppControlList) { return CPUT_ERROR_INVALID_PARAMETER; } // todo: make a copy instead to avoid deletion problems? *ppControlList = mControlPanelIDList[mActiveControlPanelSlotID]->mControlList; return CPUT_SUCCESS; } // removes specified control from the panel (does not delete the control) //----------------------------------------------------------------------------- CPUTResult CPUTGuiController::RemoveControlFromPanel(CPUTControlID controlID, CPUTControlID panelID) { CPUTControlID panelSlotID; if(CPUT_CONTROL_ID_INVALID==panelID) { // use the currently active panel if none specified panelSlotID = mActiveControlPanelSlotID; } else { panelSlotID = FindPanelIDIndex(panelID); } // invalid panel //if(CPUT_CONTROL_ID_INVALID == panelSlotID) //return CPUT_ERROR_INVALID_PARAMETER; // walk list of controls in the panel and see if control is there for(UINT i=0; imControlList.size(); i++) { if( controlID == mControlPanelIDList[panelSlotID]->mControlList[i]->GetControlID() ) { mControlPanelIDList[panelSlotID]->mControlList.erase( (mControlPanelIDList[panelSlotID]->mControlList.begin() + i) ); // trigger refresh if(mbAutoLayout) { Resize(); } return CPUT_SUCCESS; } } return CPUT_WARNING_NOT_FOUND; } // removes panel and deletes all controls associated with it //----------------------------------------------------------------------------- CPUTResult CPUTGuiController::DeletePanel(CPUTControlID panelID) { // find the panel they specified CPUTControlID panelSlotID = FindPanelIDIndex(panelID); // panel not found //if(CPUT_CONTROL_ID_INVALID == panelSlotID) //return CPUT_ERROR_INVALID_PARAMETER; // walk the panel and delete all the controls in it for(UINT i=0; imControlList.size(); i++) { // delete each control SAFE_DELETE_ARRAY(mControlPanelIDList[panelSlotID]->mControlList[i]); } // remove this panel from the control list mControlPanelIDList.erase(mControlPanelIDList.begin()+panelSlotID); // if the panel you delete is the active one, set the active panel to first // or invalid if none are left if(mActiveControlPanelSlotID == panelSlotID) { // set panel to the first panel if(0 == mControlPanelIDList.size()) mActiveControlPanelSlotID = CPUT_CONTROL_ID_INVALID; else mActiveControlPanelSlotID = 0; } // trigger refresh if(mbAutoLayout) { Resize(); } return CPUT_SUCCESS; } // private: Finds the index of the specified panel ID code in mControlPanelIDList[] //----------------------------------------------------------------------------- UINT CPUTGuiController::FindPanelIDIndex(CPUTControlID panelID) { CPUTControlID foundID = CPUT_CONTROL_ID_INVALID; for(UINT i=0; impanelID) return i; } return foundID; } // Returns the number of controls in the currently ACTIVE panel //----------------------------------------------------------------------------- int CPUTGuiController::GetNumberOfControlsInPanel(CPUTControlID panelID) { // if not specified, returns count of currently active pane if(-1 == panelID) { if(CPUT_CONTROL_ID_INVALID == mActiveControlPanelSlotID) return 0; return (int) mControlPanelIDList[mActiveControlPanelSlotID]->mControlList.size(); } // if panelID specified, return that number, or 0 if not found UINT foundID = FindPanelIDIndex(panelID); if(CPUT_CONTROL_ID_INVALID != foundID) return (int) mControlPanelIDList[foundID]->mControlList.size(); return CPUT_CONTROL_ID_INVALID; } // is the control in the panel? //----------------------------------------------------------------------------- bool CPUTGuiController::IsControlInPanel(CPUTControlID controlID, CPUTControlID panelID) { CPUTControlID panelSlotID; if(-1==panelID) { // use the currently active panel if none specified panelSlotID = mActiveControlPanelSlotID; } else { panelSlotID = FindPanelIDIndex(panelID); } // invalid panel if(CPUT_CONTROL_ID_INVALID == panelSlotID) return false; // walk list of controls in the panel and see if control is there for(UINT i=0; imControlList.size(); i++) { if( controlID == mControlPanelIDList[panelSlotID]->mControlList[i]->GetControlID() ) return true; } return false; } // Control management // Add a control (to a panel) //----------------------------------------------------------------------------- CPUTResult CPUTGuiController::AddControl(CPUTControl *pControl, CPUTControlID panelID) { //if(NULL == pControl) //return CPUT_ERROR_INVALID_PARAMETER; // set the global callback handler for this object pControl->SetControlCallback(mpHandler); CPUTControlID panelSlotID = FindPanelIDIndex(panelID); // if the panel wasn't found, add a new one if(CPUT_CONTROL_ID_INVALID == panelSlotID) { Panel *pNewControlPanel = new Panel(); pNewControlPanel->mpanelID = panelID; pNewControlPanel->mControlList.clear(); pNewControlPanel->mpFocusControl = NULL; mControlPanelIDList.push_back( pNewControlPanel ); panelSlotID = (int)mControlPanelIDList.size()-1; // make the newly added panel active if none was // active before if(CPUT_CONTROL_ID_INVALID == mActiveControlPanelSlotID) mActiveControlPanelSlotID = panelSlotID; } // store the control in the list mControlPanelIDList[panelSlotID]->mControlList.push_back(pControl); // trigger a resize to position controls optimally if(mbAutoLayout) { Resize(); } return CPUT_SUCCESS; } // returns a pointer to the specified control //----------------------------------------------------------------------------- CPUTControl* CPUTGuiController::GetControl(CPUTControlID controlID, CPUTResult *pResult) { if (pResult) { *pResult = CPUT_SUCCESS; } for(UINT i=0; imControlList.size(); j++) { if(controlID == mControlPanelIDList[i]->mControlList[j]->GetControlID()) { return mControlPanelIDList[i]->mControlList[j]; } } } if (pResult) { *pResult = CPUT_GUI_INVALID_CONTROL_ID; } return NULL; } // Find control and return pointer and panel id for it //----------------------------------------------------------------------------- CPUTResult CPUTGuiController::FindControl(CPUTControlID controlID, CPUTControl **ppControl, CPUTControlID *pPanelID) { //if((NULL==ppControl) || (NULL==pPanelID)) //return CPUT_ERROR_INVALID_PARAMETER; for(UINT i=0; imControlList.size(); j++) { if(controlID == mControlPanelIDList[i]->mControlList[j]->GetControlID()) { // found it! *pPanelID = mControlPanelIDList[i]->mpanelID; *ppControl = mControlPanelIDList[i]->mControlList[j]; return CPUT_SUCCESS; } } } return CPUT_ERROR_NOT_FOUND; } // Delete all the controls in the list //----------------------------------------------------------------------------- void CPUTGuiController::DeleteAllControls() { // set active panel to invalid mActiveControlPanelSlotID = CPUT_CONTROL_ID_INVALID; // walk list of panels deleting each list of controls int panelCount = (int) mControlPanelIDList.size(); for(int i=0; imControlList.size(); for(int j=0; jmControlList[j] ); } // erase this panel's control list mControlPanelIDList[i]->mControlList.clear(); // delete the panel object SAFE_DELETE( mControlPanelIDList[i] ); mControlPanelIDList[i] = NULL; } // clear the panel list mControlPanelIDList.clear(); // trigger refresh if(mbAutoLayout) { Resize(); } } // Flag the GUI system that a control has changed shape/size // and that it needs to recalculate it's layout on the next render //-------------------------------------------------------------------------------- void CPUTGuiController::Resize() { mRecalculateLayout = true; mUberBufferDirty = true; } // Re-calculates all the positions of the controls based on their sizes // to have a 'pretty' layout //-------------------------------------------------------------------------------- void CPUTGuiController::RecalculateLayout() { // if we have no valid panel, just return if(CPUT_CONTROL_ID_INVALID == mActiveControlPanelSlotID) { return; } // if we don't want the auto-layout feature, just return if(false == mbAutoLayout) { return; } // get window size CPUT_RECT windowRect; CPUTOSServices *pServices = CPUTOSServices::GetOSServices(); pServices->GetClientDimensions(&windowRect.x, &windowRect.y, &windowRect.width, &windowRect.height); // Build columns of controls right to left int x,y; x=0; y=0; // walk list of controls, counting up their *heights*, until the // column is full. While counting, keep track of the *widest* int width, height; const int GUI_WINDOW_PADDING = 5; int numberOfControls = (int) mControlPanelIDList[mActiveControlPanelSlotID]->mControlList.size(); int indexStart=0; int indexEnd=0; int columnX = 0; int columnNumber = 1; while(indexEnd < numberOfControls) { int columnWidth=0; y=0; // figure out which controls belong in this column + column width while( indexEnd < numberOfControls ) { if(mControlPanelIDList[mActiveControlPanelSlotID]->mControlList[indexEnd]->IsVisible() && mControlPanelIDList[mActiveControlPanelSlotID]->mControlList[indexEnd]->IsAutoArranged()) { mControlPanelIDList[mActiveControlPanelSlotID]->mControlList[indexEnd]->GetDimensions(width, height); if( y + height + GUI_WINDOW_PADDING < (windowRect.height-2*GUI_WINDOW_PADDING)) { y = y + height + GUI_WINDOW_PADDING; if(columnWidth < width) { columnWidth = width; } indexEnd++; } else { // if the window is now so small it won't fit a whole control, just // draw one anyway and it'll just have to be clipped if(indexEnd == indexStart) { columnWidth = width; indexEnd++; } break; } } else { indexEnd++; } } // ok, now re-position each control with x at widest, and y at proper height y=GUI_WINDOW_PADDING; for(int i=indexStart; imControlList[i]->IsVisible() && mControlPanelIDList[mActiveControlPanelSlotID]->mControlList[i]->IsAutoArranged()) { mControlPanelIDList[mActiveControlPanelSlotID]->mControlList[i]->GetDimensions(width, height); x = windowRect.width - columnX - columnWidth - (columnNumber*GUI_WINDOW_PADDING); mControlPanelIDList[mActiveControlPanelSlotID]->mControlList[i]->SetPosition(x,y); y = y + height + GUI_WINDOW_PADDING; } } indexStart = indexEnd; columnX+=columnWidth; columnNumber++; } mRecalculateLayout = false; } // Sets the object to call back for all newly created objects // if ForceAll=true, then it walks the list of all the registered controls // in all the panels and resets their callbacks //----------------------------------------------------------------------------- void CPUTGuiController::SetCallback(CPUTCallbackHandler *pHandler, bool ForceAll) { if(true == ForceAll) { // walk list of ALL the controls and reset the callback pointer int panelCount = (int) mControlPanelIDList.size(); for(int i=0; imControlList.size(); for(int j=0; jmControlList[j]->SetControlCallback(pHandler); } } } else { // set the callback handler to be used on any NEW controls added mpHandler = pHandler; } } ================================================ FILE: CPUT/CPUT/CPUTGuiController.h ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #ifndef __CPUTGUICONTROLLER_H__ #define __CPUTGUICONTROLLER_H__ #include #include // for RAND_MAX #include #include "CPUTEventHandler.h" #include "CPUTControl.h" #include "CPUTOSServicesWin.h" // definition of the vertex used in the GUI shader struct CPUTGUIVertex { float3 Pos; float2 UV; CPUTColor4 Color; }; class CPUTGuiController:public CPUTEventHandler { public: CPUTGuiController(); virtual ~CPUTGuiController(); // get GUI controller base class static CPUTGuiController *GetController(); //CPUTEventHandler members virtual CPUTEventHandledCode HandleKeyboardEvent(CPUTKey key) {UNREFERENCED_PARAMETER(key); return CPUT_EVENT_UNHANDLED;} virtual CPUTEventHandledCode HandleMouseEvent(int x, int y, int wheel, CPUTMouseState state); // members void EnableAutoLayout(bool UseAutoLayout); CPUTResult SetResourceDirectory(const cString ResourceDirectory); // sets the resource directory to use when loading GUI resources void GetResourceDirectory(cString &ResourceDirectory); // sets the resource directory to use when loading GUI resources // Panels CPUTResult AddControl(CPUTControl *pControl, CPUTControlID panelID); // adds a control to the specified panel CPUTResult FindControl(CPUTControlID controlID, CPUTControl **ppControl, CPUTControlID *pPanelID); // search all panels to find a control and its panelID CPUTControl *GetControl(CPUTControlID controlID, CPUTResult *pResult = NULL); // search all panels to find a control CPUTResult SetActivePanel(CPUTControlID panelID); // sets the actively displayed panel CPUTControlID GetActivePanelID(); // returns the ID of the active panel CPUTResult GetActiveControlList(std::vector *ppControlList); CPUTResult RemoveControlFromPanel(CPUTControlID controlID, CPUTControlID panelID=-1); // removes specified control from the panel (does not delete the control) CPUTResult DeletePanel(CPUTControlID panelID); // removes panel and deletes all controls associated with it void DeleteAllControls(); // deletes all controls and all panels int GetNumberOfControlsInPanel(CPUTControlID panelID=-1); // returns the number of controls in a specific panel bool IsControlInPanel(CPUTControlID controlID, CPUTControlID panelID=-1); // Is specified control in panel? bool IsRecalculatingLayout() {return mRecalculateLayout;} // drawing/callbacks void Resize(); void RecalculateLayout(); // forces a recalculation of control positions void SetCallback(CPUTCallbackHandler *pHandler, bool ForceAll=false); // sets the event handler callback on all registered controls void ControlIsDirty() {mUberBufferDirty = true;} protected: cString mResourceDirectory; bool mUberBufferDirty; bool mRecalculateLayout; struct Panel { CPUTControlID mpanelID; CPUTControl *mpFocusControl; std::vector mControlList; }; // list of panels which have lists of controls associated with it std::vector mControlPanelIDList; // the active panel list CPUTControlID mActiveControlPanelSlotID; CPUTCallbackHandler *mpHandler; // active control CPUTControl *mpFocusControl; bool mbAutoLayout; private: bool mbRebuildDrawList; // helper functions UINT FindPanelIDIndex(CPUTControlID panelID); }; #endif //#ifndef __CPUTGUICONTROLLER_H__ ================================================ FILE: CPUT/CPUT/CPUTGuiControllerDX11.cpp ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #include "CPUTGuiControllerDX11.h" #include "CPUT_DX11.h" // for CPUTSetRasterizerState() #include "CPUTTextureDX11.h" #include "CPUTFontDX11.h" CPUTGuiControllerDX11* CPUTGuiControllerDX11::mguiController = NULL; // chained constructor //-------------------------------------------------------------------------------- CPUTGuiControllerDX11::CPUTGuiControllerDX11():CPUTGuiController(), mpGUIVertexShader(NULL), mpGUIPixelShader(NULL), mpConstantBufferVS(NULL), mpVertexLayout(NULL), // texture atlas+uber buffer mpControlTextureAtlas(NULL), mpControlTextureAtlasView(NULL), mpUberBuffer(NULL), mpMirrorBuffer(NULL), mUberBufferIndex(0), mUberBufferMax(CPUT_GUI_BUFFER_SIZE), mpFont(NULL), mpTextTextureAtlas(NULL), mpTextTextureAtlasView(NULL), mpTextUberBuffer(NULL), mpTextMirrorBuffer(NULL), mTextUberBufferIndex(0), mFocusedControlBufferIndex(0), mpFocusedControlBuffer(NULL), mFocusedControlTextBufferIndex(0), mpFocusedControlTextBuffer(NULL), mpGUIRenderStateBlock(NULL), mbDrawFPS(false), mLastFPS(0), mpFPSCounter(NULL), mpFPSMirrorBuffer(NULL), mFPSBufferIndex(0), mpFPSDirectXBuffer(NULL), mpFPSTimer(NULL) { mpMirrorBuffer = new CPUTGUIVertex[CPUT_GUI_BUFFER_SIZE]; mpTextMirrorBuffer = new CPUTGUIVertex[CPUT_GUI_BUFFER_STRING_SIZE]; mpFocusedControlMirrorBuffer = new CPUTGUIVertex[CPUT_GUI_BUFFER_SIZE]; mpFocusedControlTextMirrorBuffer = new CPUTGUIVertex[CPUT_GUI_BUFFER_STRING_SIZE]; mpFPSMirrorBuffer = new CPUTGUIVertex[CPUT_GUI_BUFFER_STRING_SIZE]; mpFPSTimer = new CPUTTimerWin(); #ifdef SAVE_RESTORE_DSHSGS_SHADER_STATE mpGeometryShaderState=NULL; mpGeometryShaderClassInstances=NULL; mGeometryShaderNumClassInstances=0; mpHullShaderState=NULL; mpHullShaderClassInstances=NULL; UINT mHullShaderNumClassInstance=0; mpDomainShaderState=NULL; mpDomainShaderClassIntances=NULL; UINT mDomainShaderNumClassInstances=0; #endif } // destructor //-------------------------------------------------------------------------------- CPUTGuiControllerDX11::~CPUTGuiControllerDX11() { // delete all the controls under you ReleaseResources(); DeleteAllControls(); // FPS counter SAFE_DELETE(mpFPSCounter); SAFE_DELETE(mpFPSTimer); // delete arrays SAFE_DELETE_ARRAY(mpTextMirrorBuffer); SAFE_DELETE_ARRAY(mpMirrorBuffer); SAFE_DELETE_ARRAY(mpFocusedControlMirrorBuffer); SAFE_DELETE_ARRAY(mpFocusedControlTextMirrorBuffer); SAFE_DELETE_ARRAY(mpFPSMirrorBuffer); } // static getter //-------------------------------------------------------------------------------- CPUTGuiControllerDX11* CPUTGuiControllerDX11::GetController() { if(NULL==mguiController) mguiController = new CPUTGuiControllerDX11(); return mguiController; } // Delete the controller //-------------------------------------------------------------------------------- CPUTResult CPUTGuiControllerDX11::DeleteController() { SAFE_DELETE(mguiController); return CPUT_SUCCESS; } //-------------------------------------------------------------------------------- CPUTResult CPUTGuiControllerDX11::ReleaseResources() { //Release all allocated resources SAFE_RELEASE(mpGUIVertexShader); SAFE_RELEASE(mpGUIPixelShader); SAFE_RELEASE(mpVertexLayout); SAFE_RELEASE(mpConstantBufferVS); // release the texture atlas+buffers SAFE_RELEASE(mpControlTextureAtlasView); SAFE_RELEASE(mpControlTextureAtlas); SAFE_RELEASE(mpUberBuffer); // SAFE_RELEASE(mpFont->mpTextAtlas); SAFE_RELEASE(mpFont); SAFE_RELEASE(mpTextTextureAtlasView); SAFE_RELEASE(mpTextTextureAtlas); SAFE_RELEASE(mpTextUberBuffer); SAFE_RELEASE(mpFocusedControlBuffer); SAFE_RELEASE(mpFocusedControlTextBuffer); SAFE_RELEASE(mpGUIRenderStateBlock); SAFE_RELEASE(mpFPSDirectXBuffer); // tell all controls to unregister all their static resources CPUTText::UnRegisterStaticResources(); CPUTButton::UnRegisterStaticResources(); CPUTCheckbox::UnRegisterStaticResources(); CPUTSlider::UnRegisterStaticResources(); CPUTDropdown::UnRegisterStaticResources(); return CPUT_SUCCESS; } // Initialize the GUI controller and all it's static resources //-------------------------------------------------------------------------------- CPUTResult CPUTGuiControllerDX11::Initialize(ID3D11DeviceContext *pImmediateContext, cString &ResourceDirectory) { // All the GUI resource files cString VertexShader = _L(".//shaders//GUIShaderDX.vs"); cString PixelShader = _L(".//shaders//GUIShaderDX.ps"); cString DefaultFontFile = _L(".//fonts//font_arial_12.dds"); cString ControlAtlasTexture = _L(".//controls//atlas.dds"); cString RenderStateFile = _L(".//Shaders//GUIRenderState.rs"); // store the resource directory to be used by the GUI manager SetResourceDirectory(ResourceDirectory); return RegisterGUIResources(pImmediateContext, VertexShader, PixelShader, RenderStateFile, DefaultFontFile, ControlAtlasTexture); } // Control creation // Create a button control and add it to the GUI layout controller //-------------------------------------------------------------------------------- CPUTResult CPUTGuiControllerDX11::CreateButton(const cString ButtonText, CPUTControlID controlID, CPUTControlID panelID, CPUTButton **ppButton) { // create the control CPUTButton *pButton = new CPUTButton(ButtonText, controlID, mpFont); ASSERT(NULL != pButton, _L("Failed to create control.") ); // return control if requested if(NULL!=ppButton) { *ppButton = pButton; } // add control to the gui manager return this->AddControl(pButton, panelID); } // Create a slider control and add it to the GUI layout controller //-------------------------------------------------------------------------------- CPUTResult CPUTGuiControllerDX11::CreateSlider(const cString SliderText, CPUTControlID controlID, CPUTControlID panelID, CPUTSlider **ppSlider) { // create the control CPUTSlider *pSlider = new CPUTSlider(SliderText, controlID, mpFont); ASSERT(NULL!=pSlider, _L("Failed creating slider") ); // return control if requested if(NULL!=ppSlider) { *ppSlider = pSlider; } // add control to the gui manager return this->AddControl(pSlider, panelID); } // Create a checkbox control and add it to the GUI layout controller //-------------------------------------------------------------------------------- CPUTResult CPUTGuiControllerDX11::CreateCheckbox(const cString CheckboxText, CPUTControlID controlID, CPUTControlID panelID, CPUTCheckbox **ppCheckbox) { // create the control CPUTCheckbox *pCheckbox = new CPUTCheckbox(CheckboxText, controlID, mpFont); ASSERT(NULL!=pCheckbox, _L("Failed creating checkbox") ); // return control if requested if(NULL!=ppCheckbox) { *ppCheckbox = pCheckbox; } // add control to the gui manager return this->AddControl(pCheckbox, panelID); } // Create a dropdown control and add it to the GUI layout controller //-------------------------------------------------------------------------------- CPUTResult CPUTGuiControllerDX11::CreateDropdown(const cString SelectionText, CPUTControlID controlID, CPUTControlID panelID, CPUTDropdown **ppDropdown) { // create the control CPUTDropdown *pDropdown = new CPUTDropdown(SelectionText, controlID, mpFont); ASSERT(NULL!=pDropdown, _L("Failed creating control") ); // return control if requested if(NULL!=ppDropdown) { *ppDropdown = pDropdown; } // add control to the gui manager CPUTResult result; result = this->AddControl(pDropdown, panelID); return result; } // Create a text item (static text) //-------------------------------------------------------------------------------- CPUTResult CPUTGuiControllerDX11::CreateText(const cString Text, CPUTControlID controlID, CPUTControlID panelID, CPUTText **ppStatic) { // create the control CPUTText *pStatic=NULL; pStatic = new CPUTText(Text, controlID, mpFont); ASSERT(NULL!=pStatic, _L("Failed creating static") ); if(NULL != ppStatic) { *ppStatic = pStatic; } // add control to the gui manager return this->AddControl(pStatic, panelID); } // Deletes a control from the GUI manager // Will delete all instances of the control no matter which panel(s) it is in and then // deallocates the memory for the control //-------------------------------------------------------------------------------- CPUTResult CPUTGuiControllerDX11::DeleteControl(CPUTControlID controlID) { // look thruogh all the panels and delete the item with this controlID // for each panel std::vector pDeleteList; for(UINT i=0; imControlList.size(); j++) { if(controlID == mControlPanelIDList[i]->mControlList[j]->GetControlID()) { // found an instance of the control we wish to delete // see if it's in the list already bool bFound = false; for(UINT x=0; xmControlList[j] == pDeleteList[x] ) { bFound = true; break; } } if(!bFound) { // store for deleting pDeleteList.push_back( mControlPanelIDList[i]->mControlList[j] ); } // remove the control from the container list mControlPanelIDList[i]->mControlList.erase( mControlPanelIDList[i]->mControlList.begin() + j ); } } } // delete the control(s) we found with this id for(UINT i=0; iResize(); // don't worry about cleaning up std::vector list itself, it'll do so when it falls out of scope return CPUT_SUCCESS; } // DrawFPS - Should the GUI draw the FPS counter in the upper-left? //-------------------------------------------------------------------------------- void CPUTGuiControllerDX11::DrawFPS(bool drawfps) { mbDrawFPS = drawfps; } // GetFPS - Returns the last frame's FPS value //-------------------------------------------------------------------------------- float CPUTGuiControllerDX11::GetFPS() { return mLastFPS; } // Draw - must be positioned after all the controls are defined //-------------------------------------------------------------------------------- void CPUTGuiControllerDX11::Draw(ID3D11DeviceContext *pImmediateContext) { HEAPCHECK; if( 0 != GetNumberOfControlsInPanel()) { SetGUIDrawingState(pImmediateContext); } else { return; } ID3D11VertexShader *pVertexShader = mpGUIVertexShader->GetNativeVertexShader(); ID3D11PixelShader *pPixelShader = mpGUIPixelShader->GetNativePixelShader(); // check and see if any of the controls resized themselves int ControlCount=GetNumberOfControlsInPanel(); bool ResizingNeeded = false; for(int ii=0; iimControlList[ii]; if(true == pControl->ControlResizedItself()) { ResizingNeeded = true; pControl->ControlResizingHandled(); } } // if any of the controls resized, then re-do the autoarrangment if(true == ResizingNeeded) { this->Resize(); } // Now check to see if any controls' graphics are dirty for(int ii=0; iimControlList[ii]; if(true == pControl->ControlGraphicsDirty()) { mUberBufferDirty = true; break; } } // if any of the controls have announced they are dirty, then rebuild the mirror buffer and update the GFX buffer if(mUberBufferDirty) { // if a resize was flagged, do it now. if(mRecalculateLayout) { RecalculateLayout(); } // 'clear' the buffer by resetting the pointer to the head mUberBufferIndex = 0; mTextUberBufferIndex = 0; mFocusedControlBufferIndex = 0; mFocusedControlTextBufferIndex = 0; int ii=0; while(iimControlList[ii]; // don't draw the focus control - draw it last so it stays on 'top' if(mpFocusControl != pControl) { switch(pControl->GetType()) { case CPUT_BUTTON: ((CPUTButton*)pControl)->DrawIntoBuffer(mpMirrorBuffer, &mUberBufferIndex, mUberBufferMax, mpTextMirrorBuffer, &mTextUberBufferIndex, CPUT_GUI_BUFFER_STRING_SIZE); break; case CPUT_CHECKBOX: ((CPUTCheckbox*)pControl)->DrawIntoBuffer(mpMirrorBuffer, &mUberBufferIndex, mUberBufferMax, mpTextMirrorBuffer, &mTextUberBufferIndex, CPUT_GUI_BUFFER_STRING_SIZE); break; case CPUT_SLIDER: ((CPUTSlider*)pControl)->DrawIntoBuffer(mpMirrorBuffer, &mUberBufferIndex, mUberBufferMax, mpTextMirrorBuffer, &mTextUberBufferIndex, CPUT_GUI_BUFFER_STRING_SIZE); break; case CPUT_DROPDOWN: ((CPUTDropdown*)pControl)->DrawIntoBuffer(mpMirrorBuffer, &mUberBufferIndex, mUberBufferMax, mpTextMirrorBuffer, &mTextUberBufferIndex, CPUT_GUI_BUFFER_STRING_SIZE); break; case CPUT_STATIC: ((CPUTText*)pControl)->DrawIntoBuffer(mpTextMirrorBuffer, &mTextUberBufferIndex, CPUT_GUI_BUFFER_STRING_SIZE); break; } } ii++; HEAPCHECK } // do the 'focused' control last so it stays on top (i.e. dropdowns) if(mpFocusControl) { switch(mpFocusControl->GetType()) { case CPUT_BUTTON: ((CPUTButton*)mpFocusControl)->DrawIntoBuffer(mpFocusedControlMirrorBuffer, &mFocusedControlBufferIndex, mUberBufferMax, mpFocusedControlTextMirrorBuffer, &mFocusedControlTextBufferIndex, CPUT_GUI_BUFFER_STRING_SIZE); break; case CPUT_CHECKBOX: ((CPUTCheckbox*)mpFocusControl)->DrawIntoBuffer(mpFocusedControlMirrorBuffer, &mFocusedControlBufferIndex, mUberBufferMax, mpFocusedControlTextMirrorBuffer, &mFocusedControlTextBufferIndex, CPUT_GUI_BUFFER_STRING_SIZE); break; case CPUT_SLIDER: ((CPUTSlider*)mpFocusControl)->DrawIntoBuffer(mpFocusedControlMirrorBuffer, &mFocusedControlBufferIndex, mUberBufferMax, mpFocusedControlTextMirrorBuffer, &mFocusedControlTextBufferIndex, CPUT_GUI_BUFFER_STRING_SIZE); break; case CPUT_DROPDOWN: ((CPUTDropdown*)mpFocusControl)->DrawIntoBuffer(mpFocusedControlMirrorBuffer, &mFocusedControlBufferIndex, mUberBufferMax, mpFocusedControlTextMirrorBuffer, &mFocusedControlTextBufferIndex, CPUT_GUI_BUFFER_STRING_SIZE); break; case CPUT_STATIC: ((CPUTText*)mpFocusControl)->DrawIntoBuffer(mpFocusedControlMirrorBuffer, &mFocusedControlTextBufferIndex, CPUT_GUI_BUFFER_STRING_SIZE); break; } } // update the uber-buffers with the control graphics UpdateUberBuffers(pImmediateContext); // Clear dirty flag on uberbuffer mUberBufferDirty = false; } HEAPCHECK // calculate the fps double elapsed = mpFPSTimer->GetElapsedTime(); double fps = 1.0 / elapsed; mLastFPS = (float) fps; // if we're drawing the FPS counter - update that // We do this independently of uber-buffer updates since we'll have FPS updates every frame, // but likely not have control updates every frame if(mbDrawFPS) { // calculate the time elapsed since last frame bool UberBufferWasDirty = mUberBufferDirty; cString Data; { wchar_t wcstring[CPUT_MAX_STRING_LENGTH]; swprintf_s(&wcstring[0], CPUT_MAX_STRING_LENGTH, _L("%.2f"), fps); Data=wcstring; } // build the FPS string cString FPS = _L("FPS: ")+Data; mpFPSCounter->SetText(FPS); // 'draw' the string into the buffer mFPSBufferIndex = 0; mpFPSCounter->DrawIntoBuffer(mpFPSMirrorBuffer, &mFPSBufferIndex, CPUT_GUI_BUFFER_STRING_SIZE); // update the DirectX vertex buffer ASSERT(CPUT_GUI_BUFFER_STRING_SIZE > mFocusedControlTextBufferIndex, _L("CPUT GUI: Too many strings for default-sized uber-buffer. Increase CPUT_GUI_BUFFER_STRING_SIZE")); pImmediateContext->UpdateSubresource(mpFPSDirectXBuffer, 0, NULL, mpFPSMirrorBuffer, mFPSBufferIndex*sizeof(CPUTGUIVertex), 0); // start next frame timer mpFPSTimer->StartTimer(); if(false == UberBufferWasDirty) { mUberBufferDirty = false; } } // set up orthographic display int windowWidth, windowHeight; GUIConstantBufferVS ConstantBufferMatrices; float znear = 0.1f; float zfar = 100.0f; XMMATRIX m; CPUTOSServices *pServices = CPUTOSServices::GetOSServices(); pServices->GetClientDimensions( &windowWidth, &windowHeight ); m = XMMatrixOrthographicOffCenterLH(0, (float)windowWidth, (float)windowHeight, 0, znear, zfar); ConstantBufferMatrices.Projection = XMMatrixTranspose( m ); // set the vertex shader pImmediateContext->VSSetShader( pVertexShader, NULL, 0 ); UINT VertexStride = sizeof(CPUTGUIVertex); UINT VertexOffset = 0; pImmediateContext->IASetVertexBuffers( 0, 1, &mpUberBuffer, &VertexStride, &VertexOffset ); m = XMMatrixIdentity(); ConstantBufferMatrices.Model = XMMatrixTranspose( m ); pImmediateContext->UpdateSubresource( mpConstantBufferVS, 0, NULL, &ConstantBufferMatrices, 0, 0 ); pImmediateContext->VSSetConstantBuffers( 0, 1, &mpConstantBufferVS ); // -- draw the normal controls -- // draw the control graphics pImmediateContext->PSSetShader( pPixelShader, NULL, 0 ); pImmediateContext->PSSetShaderResources( 0, 1, &mpControlTextureAtlasView ); pImmediateContext->Draw(mUberBufferIndex,0); // draw the control's text pImmediateContext->PSSetShaderResources( 0, 1, &mpTextTextureAtlasView ); pImmediateContext->IASetVertexBuffers( 0, 1, &mpTextUberBuffer, &VertexStride, &VertexOffset ); // draw the text uber-buffer pImmediateContext->Draw(mTextUberBufferIndex,0); // draw the FPS counter if(mbDrawFPS) { pImmediateContext->IASetVertexBuffers( 0, 1, &mpFPSDirectXBuffer, &VertexStride, &VertexOffset ); pImmediateContext->Draw(mFPSBufferIndex, 0); } // -- draw the focused control -- // Draw the focused control's graphics pImmediateContext->PSSetShader( pPixelShader, NULL, 0 ); pImmediateContext->PSSetShaderResources( 0, 1, &mpControlTextureAtlasView ); pImmediateContext->IASetVertexBuffers( 0, 1, &mpFocusedControlBuffer, &VertexStride, &VertexOffset ); // draw the uber-buffer pImmediateContext->Draw(mFocusedControlBufferIndex,0); // Draw the focused control's text pImmediateContext->PSSetShaderResources( 0, 1, &mpTextTextureAtlasView ); pImmediateContext->IASetVertexBuffers( 0, 1, &mpFocusedControlTextBuffer, &VertexStride, &VertexOffset ); // draw the text uber-buffer pImmediateContext->Draw(mFocusedControlTextBufferIndex,0); // restore the drawing state ClearGUIDrawingState(pImmediateContext); HEAPCHECK; } // //------------------------------------------------------------------------ CPUTResult CPUTGuiControllerDX11::UpdateUberBuffers(ID3D11DeviceContext *pImmediateContext ) { ASSERT(pImmediateContext, _L("CPUTGuiControllerDX11::UpdateUberBuffers - Context pointer is NULL")); // get the device ID3D11Device *pD3dDevice = NULL; pImmediateContext->GetDevice(&pD3dDevice); // Update geometry to draw the control graphics ASSERT(CPUT_GUI_VERTEX_BUFFER_SIZE > mUberBufferIndex, _L("CPUT GUI: Too many controls for default-sized uber-buffer. Increase CPUT_GUI_VERTEX_BUFFER_SIZE")); pImmediateContext->UpdateSubresource(mpUberBuffer, 0, NULL, (void*) mpMirrorBuffer, sizeof( CPUTGUIVertex )*(mUberBufferIndex+1), 0); // Update geometry to draw the controls' text ASSERT(CPUT_GUI_BUFFER_STRING_SIZE > mTextUberBufferIndex, _L("CPUT GUI: Too many strings for default-sized uber-buffer. Increase CPUT_GUI_BUFFER_STRING_SIZE")); pImmediateContext->UpdateSubresource(mpTextUberBuffer, 0, NULL, (void*) mpTextMirrorBuffer, sizeof( CPUTGUIVertex )*(mTextUberBufferIndex+1), 0); // register the focused control's graphics ASSERT(CPUT_GUI_VERTEX_BUFFER_SIZE > mUberBufferIndex, _L("CPUT GUI: Too many controls for default-sized uber-buffer. Increase CPUT_GUI_VERTEX_BUFFER_SIZE")); pImmediateContext->UpdateSubresource(mpFocusedControlBuffer, 0, NULL, (void*) mpFocusedControlMirrorBuffer, sizeof( CPUTGUIVertex )*(mFocusedControlBufferIndex+1), 0); //register the focused control's text ASSERT(CPUT_GUI_BUFFER_STRING_SIZE > mFocusedControlTextBufferIndex, _L("CPUT GUI: Too many strings for default-sized uber-buffer. Increase CPUT_GUI_BUFFER_STRING_SIZE")); pImmediateContext->UpdateSubresource(mpFocusedControlTextBuffer, 0, NULL, (void*) mpFocusedControlTextMirrorBuffer, sizeof( CPUTGUIVertex )*(mFocusedControlTextBufferIndex+1), 0); // release the device pointer SAFE_RELEASE(pD3dDevice); return CPUT_SUCCESS; } // Set the state for drawing the gui //----------------------------------------------------------------------------- void CPUTGuiControllerDX11::SetGUIDrawingState(ID3D11DeviceContext *pImmediateContext) { // set the GUI shaders as active ID3D11VertexShader *pVertexShader = mpGUIVertexShader->GetNativeVertexShader(); pImmediateContext->VSSetShader( pVertexShader, NULL, 0 ); //D3D11_VIEWPORT viewport = { 0.0f, 0.0f, (float)mWidth, (float)mHeight, 0.0f, 1.0f }; //((CPUTRenderParametersDX*)&renderParams)->mpContext->RSSetViewports( 1, &viewport ); #ifdef SAVE_RESTORE_DS_HS_GS_SHADER_STATE pImmediateContext->GSGetShader(&mpGeometryShaderState, &mpGeometryShaderClassInstances, &mGeometryShaderNumClassInstances); pImmediateContext->HSGetShader(&mpHullShaderState, &mpHullShaderClassInstances, &mHullShaderNumClassInstance); pImmediateContext->DSGetShader(&mpDomainShaderState, &mpDomainShaderClassIntances, &mDomainShaderNumClassInstances); #endif // set the geometry, hull, and domain shaders to null (in case the user had set them) // since the GUI system doesn't need them pImmediateContext->GSSetShader(NULL, NULL, 0); pImmediateContext->HSSetShader(NULL, NULL, 0); pImmediateContext->DSSetShader(NULL, NULL, 0); // set topology to triangle list pImmediateContext->IAGetPrimitiveTopology(&mTopology); pImmediateContext->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST); // Use CPUTRenderStateBlock to set the render state for GUI drawing CPUTRenderParametersDX renderParams; renderParams.mpContext = pImmediateContext; mpGUIRenderStateBlock->SetRenderStates(renderParams); // update the constant buffer matrices // set for orthographic perspective GUIConstantBufferVS cb; cb.Model = XMMatrixIdentity(); cb.Projection = XMMatrixIdentity(); pImmediateContext->UpdateSubresource( mpConstantBufferVS, 0, NULL, &cb, 0, 0 ); pImmediateContext->VSSetConstantBuffers( 0, 1, &mpConstantBufferVS ); // set the input layout pImmediateContext->IASetInputLayout( mpVertexLayout ); // set the pixel shader pImmediateContext->PSSetShader( mpGUIPixelShader->GetNativePixelShader(), NULL, 0 ); } // Restores the previous state //----------------------------------------------------------------------------- void CPUTGuiControllerDX11::ClearGUIDrawingState(ID3D11DeviceContext *pImmediateContext) { // restore drawing topology mode that was in use before entering the gui drawing state pImmediateContext->IASetPrimitiveTopology(mTopology); #ifdef SAVE_RESTORE_DS_HS_GS_SHADER_STATE pImmediateContext->GSSetShader(mpGeometryShaderState, (ID3D11ClassInstance* const*)&mpGeometryShaderClassInstances, mGeometryShaderNumClassInstances); pImmediateContext->HSSetShader(mpHullShaderState, (ID3D11ClassInstance* const*)&mpHullShaderClassInstances, mHullShaderNumClassInstance); pImmediateContext->DSSetShader(mpDomainShaderState, (ID3D11ClassInstance* const*)&mpDomainShaderClassIntances, mDomainShaderNumClassInstances); #endif } // Load and register all the resources needed by the GUI system //----------------------------------------------------------------------------- CPUTResult CPUTGuiControllerDX11::RegisterGUIResources(ID3D11DeviceContext *pImmediateContext, cString VertexShaderFilename, cString PixelShaderFilename, cString RenderStateFile, cString DefaultFontFilename, cString ControlAtlasTexture) { if(NULL==pImmediateContext) { return CPUT_ERROR_INVALID_PARAMETER; } CPUTResult result; HRESULT hr; ID3D11Device *pD3dDevice = NULL; CPUTOSServices *pServices = NULL; CPUTAssetLibraryDX11 *pAssetLibrary = NULL; cString ErrorMessage; // Get the services/resource pointers we need pServices = CPUTOSServices::GetOSServices(); pImmediateContext->GetDevice(&pD3dDevice); pAssetLibrary = (CPUTAssetLibraryDX11*)CPUTAssetLibraryDX11::GetAssetLibrary(); // Get the resource directory cString ResourceDirectory; CPUTGuiControllerDX11::GetController()->GetResourceDirectory(ResourceDirectory); // 1. Load the renderstate configuration for the GUI system mpGUIRenderStateBlock = (CPUTRenderStateBlockDX11*) pAssetLibrary->GetRenderStateBlock(ResourceDirectory+RenderStateFile); ASSERT(mpGUIRenderStateBlock, _L("Error loading the render state file (.rs) needed for the CPUT GUI system")); // 2. Store the shader path from AssetLibrary, change it to OUR resource directory cString OriginalAssetLibraryDirectory = pAssetLibrary->GetShaderDirectory(); pAssetLibrary->SetShaderDirectoryName(ResourceDirectory); // 3. load the shaders for gui drawing // Load the GUI Vertex Shader cString FullPath, FinalPath; FullPath = mResourceDirectory + VertexShaderFilename; pServices->ResolveAbsolutePathAndFilename(FullPath, &FinalPath); result = pAssetLibrary->GetVertexShader(FinalPath, pD3dDevice, _L("VS"), _L("vs_4_0"), &mpGUIVertexShader, true); CPUTSetDebugName( mpGUIVertexShader->GetNativeVertexShader(), _L("GUIVertexShader")); if(CPUTFAILED(result)) { ASSERT(CPUTSUCCESS(result), _L("Error loading the vertex shader needed for the CPUT GUI system.")); } ID3DBlob *pVertexShaderBlob = mpGUIVertexShader->GetBlob(); // Load the GUI Pixel Shader FullPath = mResourceDirectory + PixelShaderFilename; pServices->ResolveAbsolutePathAndFilename(FullPath, &FinalPath); result = pAssetLibrary->GetPixelShader(FinalPath, pD3dDevice, _L("PS"), _L("ps_4_0"), &mpGUIPixelShader, true); CPUTSetDebugName( mpGUIPixelShader->GetNativePixelShader(), _L("GUIPixelShader")); if(CPUTFAILED(result)) { ASSERT(CPUTSUCCESS(result), _L("Error loading the pixel shader needed for the CPUT GUI system.")); } // Restore the previous shader directory pAssetLibrary->SetShaderDirectoryName(OriginalAssetLibraryDirectory); // 4. Create the vertex layout description for all the GUI controls we'll draw // set vertex shader as active so we can configure it ID3D11VertexShader *pVertexShader = mpGUIVertexShader->GetNativeVertexShader(); pImmediateContext->VSSetShader( pVertexShader, NULL, 0 ); D3D11_INPUT_ELEMENT_DESC layout[] = { { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }, { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 }, { "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 20, D3D11_INPUT_PER_VERTEX_DATA, 0 }, }; UINT numElements = ARRAYSIZE( layout ); // Create the input layout hr = pD3dDevice->CreateInputLayout( layout, numElements, pVertexShaderBlob->GetBufferPointer(), pVertexShaderBlob->GetBufferSize(), &mpVertexLayout ); ASSERT( SUCCEEDED(hr), _L("Error creating CPUT GUI system input layout" )); CPUTSetDebugName( mpVertexLayout, _L("CPUT GUI InputLayout object")); // 5. create the vertex shader constant buffer pointers D3D11_BUFFER_DESC bd; ZeroMemory( &bd, sizeof(bd) ); bd.Usage = D3D11_USAGE_DEFAULT; bd.ByteWidth = sizeof(GUIConstantBufferVS); bd.BindFlags = D3D11_BIND_CONSTANT_BUFFER; bd.CPUAccessFlags = 0; hr = pD3dDevice->CreateBuffer( &bd, NULL, &mpConstantBufferVS ); ASSERT( SUCCEEDED(hr), _L("Error creating constant buffer VS" )); CPUTSetDebugName( mpConstantBufferVS, _L("GUI ConstantBuffer")); // Set the texture directory for loading the control texture atlas pAssetLibrary->SetTextureDirectoryName(ResourceDirectory); // load the control atlas mpControlTextureAtlas = (CPUTTextureDX11*) pAssetLibrary->GetTexture(ControlAtlasTexture); if(NULL==mpControlTextureAtlas) { return CPUT_TEXTURE_LOAD_ERROR; } mpControlTextureAtlasView = mpControlTextureAtlas->GetShaderResourceView(); mpControlTextureAtlasView->AddRef(); // restore the asset library's texture directory pAssetLibrary->SetTextureDirectoryName(OriginalAssetLibraryDirectory); // 6. Load the font atlas // store the existing asset library font directory OriginalAssetLibraryDirectory = pAssetLibrary->GetFontDirectory(); // set font directory to the resource directory pAssetLibrary->SetFontDirectoryName(ResourceDirectory); mpFont = (CPUTFontDX11*) pAssetLibrary->GetFont(DefaultFontFilename); if(NULL==mpFont) { return CPUT_TEXTURE_LOAD_ERROR; } mpTextTextureAtlas = mpFont->GetAtlasTexture(); mpTextTextureAtlas->AddRef(); mpTextTextureAtlasView = mpFont->GetAtlasTextureResourceView(); mpTextTextureAtlasView->AddRef(); // restore the asset library's font directory pAssetLibrary->SetTextureDirectoryName(OriginalAssetLibraryDirectory); // 7. Set up the DirectX uber-buffers that the controls draw into int maxSize = max(CPUT_GUI_BUFFER_STRING_SIZE, CPUT_GUI_BUFFER_SIZE); maxSize = max(maxSize, CPUT_GUI_VERTEX_BUFFER_SIZE); maxSize *= sizeof( CPUTGUIVertex ); char *pZeroedBuffer= new char[maxSize]; memset(pZeroedBuffer, 0, maxSize); // set up buffer description ZeroMemory( &bd, sizeof(bd) ); bd.Usage = D3D11_USAGE_DEFAULT; bd.ByteWidth = sizeof( CPUTGUIVertex ) * CPUT_GUI_VERTEX_BUFFER_SIZE; //mUberBufferIndex; bd.BindFlags = D3D11_BIND_VERTEX_BUFFER; bd.CPUAccessFlags = 0; // initialization data (all 0's for now) D3D11_SUBRESOURCE_DATA InitData; ZeroMemory( &InitData, sizeof(InitData) ); InitData.pSysMem = mpMirrorBuffer; // mpUberBuffer SAFE_RELEASE(mpUberBuffer); hr = pD3dDevice->CreateBuffer( &bd, &InitData, &mpUberBuffer ); ASSERT( !FAILED( hr ), _L("CPUT GUI FPS counter buffer creation failure")); CPUTSetDebugName(mpUberBuffer, _L("CPUT GUI: Control's main vertex buffer")); // mpTextUberBuffer bd.ByteWidth = sizeof( CPUTGUIVertex ) * CPUT_GUI_BUFFER_STRING_SIZE; hr = pD3dDevice->CreateBuffer( &bd, &InitData, &mpTextUberBuffer ); ASSERT( !FAILED( hr ), _L("CPUT GUI FPS counter buffer creation failure")); CPUTSetDebugName(mpTextUberBuffer, _L("CPUT GUI: control text vertex buffer")); // mpFocusedControlBuffer bd.ByteWidth = sizeof( CPUTGUIVertex ) * CPUT_GUI_VERTEX_BUFFER_SIZE; hr = pD3dDevice->CreateBuffer( &bd, &InitData, &mpFocusedControlBuffer ); ASSERT( !FAILED( hr ), _L("CPUT GUI FPS counter buffer creation failure")); CPUTSetDebugName(mpFocusedControlBuffer, _L("CPUT GUI: focused control images vertex buffer")); // mpFocusedControlTextBuffer bd.ByteWidth = sizeof( CPUTGUIVertex ) * CPUT_GUI_BUFFER_STRING_SIZE; //mFocusedControlTextBufferIndex; hr = pD3dDevice->CreateBuffer( &bd, &InitData, &mpFocusedControlTextBuffer ); ASSERT( !FAILED( hr ), _L("CPUT GUI FPS counter buffer creation failure")); CPUTSetDebugName(mpFocusedControlTextBuffer, _L("CPUT GUI: focused control text vertex buffer")); // mpFPSDirectXBuffer bd.ByteWidth = sizeof( CPUTGUIVertex ) * CPUT_GUI_BUFFER_STRING_SIZE; hr = pD3dDevice->CreateBuffer( &bd, &InitData, &mpFPSDirectXBuffer ); ASSERT( !FAILED( hr ), _L("CPUT GUI FPS counter buffer creation failure")); CPUTSetDebugName(mpFPSDirectXBuffer, _L("CPUT GUI: FPS display text")); // no longer need the device - release it. SAFE_RELEASE(pD3dDevice); SAFE_DELETE_ARRAY(pZeroedBuffer); // 8. Register all GUI sub-resources // Walk all the controls/fonts and have them register all their required static resources // Returning errors if you couldn't find your resources result = CPUTText::RegisterStaticResources(); if(CPUTFAILED(result)) { return result; } result = CPUTButton::RegisterStaticResources(); if(CPUTFAILED(result)) { return result; } result = CPUTCheckbox::RegisterStaticResources(); if(CPUTFAILED(result)) { return result; } result = CPUTSlider::RegisterStaticResources(); if(CPUTFAILED(result)) { return result; } result = CPUTDropdown::RegisterStaticResources(); if(CPUTFAILED(result)) { return result; } // create the FPS CPUTText object for drawing FPS mpFPSCounter = new CPUTText(_L("FPS:"), ID_CPUT_GUI_FPS_COUNTER, mpFont); mpFPSCounter->SetAutoArranged(false); mpFPSCounter->SetPosition(0,0); // start the timer mpFPSTimer->StartTimer(); // done return CPUT_SUCCESS; } ================================================ FILE: CPUT/CPUT/CPUTGuiControllerDX11.h ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #ifndef __CPUTGUICONTROLLERDX11_H__ #define __CPUTGUICONTROLLERDX11_H__ #include "CPUTGuiController.h" #include "CPUTTimerWin.h" #include "CPUTButton.h" #include "CPUTText.h" #include "CPUTCheckbox.h" #include "CPUTSlider.h" #include "CPUTDropdown.h" #include "CPUTVertexShaderDX11.h" #include "CPUTPixelShaderDX11.h" #include "CPUTRenderStateBlockDX11.h" //#define SAVE_RESTORE_DS_HS_GS_SHADER_STATE // forward declarations class CPUT_DX11; class CPUTButton; class CPUTSlider; class CPUTCheckbox; class CPUTDropdown; class CPUTText; class CPUTTextureDX11; class CPUTFontDX11; const unsigned int CPUT_GUI_BUFFER_SIZE = 5000; // size (in number of verticies) for all GUI control graphics const unsigned int CPUT_GUI_BUFFER_STRING_SIZE = 5000; // size (in number of verticies) for all GUI string graphics const unsigned int CPUT_GUI_VERTEX_BUFFER_SIZE = 5000; const CPUTControlID ID_CPUT_GUI_FPS_COUNTER = 4000000201; // pick very random number for FPS counter string ID #include #include // for xmmatrix/et al // the GUI controller class that dispatches the rendering calls to all the buttons //-------------------------------------------------------------------------------- class CPUTGuiControllerDX11:public CPUTGuiController { struct GUIConstantBufferVS { XMMATRIX Projection; XMMATRIX Model; }; public: static CPUTGuiControllerDX11 *GetController(); static CPUTResult DeleteController(); // initialization CPUTResult Initialize(ID3D11DeviceContext *pImmediateContext, cString &ResourceDirectory); CPUTResult ReleaseResources(); // Control creation/deletion 'helpers' CPUTResult CreateButton(const cString pButtonText, CPUTControlID controlID, CPUTControlID panelID, CPUTButton **ppButton=NULL); CPUTResult CreateSlider(const cString pSliderText, CPUTControlID controlID, CPUTControlID panelID, CPUTSlider **ppSlider=NULL); CPUTResult CreateCheckbox(const cString pCheckboxText, CPUTControlID controlID, CPUTControlID panelID, CPUTCheckbox **ppCheckbox=NULL); CPUTResult CreateDropdown(const cString pSelectionText, CPUTControlID controlID, CPUTControlID panelID, CPUTDropdown **ppDropdown=NULL); CPUTResult CreateText(const cString Text, CPUTControlID controlID, CPUTControlID panelID, CPUTText **ppStatic=NULL); CPUTResult DeleteControl(CPUTControlID controlID); // draw routines void Draw(ID3D11DeviceContext *pImmediateContext); void DrawFPS(bool drawfps); float GetFPS(); private: static CPUTGuiControllerDX11 *mguiController; // singleton object // DirectX state objects for GUI drawing CPUTVertexShaderDX11 *mpGUIVertexShader; CPUTPixelShaderDX11 *mpGUIPixelShader; ID3D11InputLayout *mpVertexLayout; ID3D11Buffer *mpConstantBufferVS; GUIConstantBufferVS mModelViewMatrices; // Texture atlas CPUTTextureDX11 *mpControlTextureAtlas; ID3D11ShaderResourceView *mpControlTextureAtlasView; ID3D11Buffer *mpUberBuffer; CPUTGUIVertex *mpMirrorBuffer; UINT mUberBufferIndex; UINT mUberBufferMax; // Font atlas CPUTFontDX11 *mpFont; CPUTTextureDX11 *mpTextTextureAtlas; ID3D11ShaderResourceView *mpTextTextureAtlasView; ID3D11Buffer *mpTextUberBuffer; CPUTGUIVertex *mpTextMirrorBuffer; UINT mTextUberBufferIndex; // Focused control buffers CPUTGUIVertex *mpFocusedControlMirrorBuffer; UINT mFocusedControlBufferIndex; ID3D11Buffer *mpFocusedControlBuffer; CPUTGUIVertex *mpFocusedControlTextMirrorBuffer; UINT mFocusedControlTextBufferIndex; ID3D11Buffer *mpFocusedControlTextBuffer; // FPS bool mbDrawFPS; float mLastFPS; CPUTText *mpFPSCounter; // FPS control buffers CPUTGUIVertex *mpFPSMirrorBuffer; UINT mFPSBufferIndex; ID3D11Buffer *mpFPSDirectXBuffer; CPUTTimerWin *mpFPSTimer; // render state CPUTRenderStateBlockDX11 *mpGUIRenderStateBlock; CPUTResult UpdateUberBuffers(ID3D11DeviceContext *pImmediateContext ); #ifdef SAVE_RESTORE_DS_HS_GS_SHADER_STATE ID3D11GeometryShader *mpGeometryShaderState; ID3D11ClassInstance *mpGeometryShaderClassInstances; UINT mGeometryShaderNumClassInstances; ID3D11HullShader *mpHullShaderState; ID3D11ClassInstance *mpHullShaderClassInstances; UINT mHullShaderNumClassInstance; ID3D11DomainShader *mpDomainShaderState; ID3D11ClassInstance *mpDomainShaderClassIntances; UINT mDomainShaderNumClassInstances; #endif // members for saving render state before/after drawing gui D3D11_PRIMITIVE_TOPOLOGY mTopology; // helper functions CPUTGuiControllerDX11(); // singleton ~CPUTGuiControllerDX11(); CPUTResult RegisterGUIResources(ID3D11DeviceContext *pImmediateContext, cString VertexShaderFilename, cString RenderStateFile, cString PixelShaderFilename, cString DefaultFontFilename, cString ControlTextureAtlas); void SetGUIDrawingState(ID3D11DeviceContext *pImmediateContext); void ClearGUIDrawingState(ID3D11DeviceContext *pImmediateContext); }; #endif // #ifndef __CPUTGUICONTROLLERDX11_H__ ================================================ FILE: CPUT/CPUT/CPUTHullShaderDX11.cpp ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #include "CPUTHullShaderDX11.h" #include "CPUTAssetLibraryDX11.h" CPUTHullShaderDX11 *CPUTHullShaderDX11::CreateHullShader( const cString &name, ID3D11Device *pD3dDevice, const cString &shaderMain, const cString &shaderProfile ) { ID3DBlob *pCompiledBlob = NULL; ID3D11HullShader *pNewHullShader = NULL; CPUTAssetLibraryDX11 *pAssetLibrary = (CPUTAssetLibraryDX11*)CPUTAssetLibrary::GetAssetLibrary(); CPUTResult result = pAssetLibrary->CompileShaderFromFile(name, shaderMain, shaderProfile, &pCompiledBlob); ASSERT( CPUTSUCCESS(result), _L("Error compiling Hull shader:\n\n") ); // Create the Hull shader // TODO: Move to Hull shader class HRESULT hr = pD3dDevice->CreateHullShader( pCompiledBlob->GetBufferPointer(), pCompiledBlob->GetBufferSize(), NULL, &pNewHullShader ); ASSERT( SUCCEEDED(hr), _L("Error creating Hull shader:\n\n") ); // cString DebugName = _L("CPUTAssetLibraryDX11::GetHullShader ")+name; // CPUTSetDebugName(pNewHullShader, DebugName); CPUTHullShaderDX11 *pNewCPUTHullShader = new CPUTHullShaderDX11( pNewHullShader, pCompiledBlob ); // add shader to library pAssetLibrary->AddHullShader(name + shaderMain + shaderProfile, pNewCPUTHullShader); // pNewCPUTHullShader->Release(); // We've added it to the library, so release our reference // return the shader (and blob) return pNewCPUTHullShader; } //-------------------------------------------------------------------------------------- CPUTHullShaderDX11 *CPUTHullShaderDX11::CreateHullShaderFromMemory( const cString &name, ID3D11Device *pD3dDevice, const cString &shaderMain, const cString &shaderProfile, const char *pShaderSource ) { ID3DBlob *pCompiledBlob = NULL; ID3D11HullShader *pNewHullShader = NULL; CPUTAssetLibraryDX11 *pAssetLibrary = (CPUTAssetLibraryDX11*)CPUTAssetLibrary::GetAssetLibrary(); CPUTResult result = pAssetLibrary->CompileShaderFromMemory(pShaderSource, shaderMain, shaderProfile, &pCompiledBlob); ASSERT( CPUTSUCCESS(result), _L("Error compiling Hull shader:\n\n") ); // Create the Hull shader // TODO: Move to Hull shader class HRESULT hr = pD3dDevice->CreateHullShader( pCompiledBlob->GetBufferPointer(), pCompiledBlob->GetBufferSize(), NULL, &pNewHullShader ); ASSERT( SUCCEEDED(hr), _L("Error creating Hull shader:\n\n") ); // cString DebugName = _L("CPUTAssetLibraryDX11::GetHullShader ")+name; // CPUTSetDebugName(pNewHullShader, DebugName); CPUTHullShaderDX11 *pNewCPUTHullShader = new CPUTHullShaderDX11( pNewHullShader, pCompiledBlob ); // add shader to library pAssetLibrary->AddHullShader(name + shaderMain + shaderProfile, pNewCPUTHullShader); // pNewCPUTHullShader->Release(); // We've added it to the library, so release our reference // return the shader (and blob) return pNewCPUTHullShader; } ================================================ FILE: CPUT/CPUT/CPUTHullShaderDX11.h ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #ifndef _CPUTHULLSHADERDX11_H #define _CPUTHULLSHADERDX11_H #include "CPUT.h" #include "CPUTShaderDX11.h" class CPUTHullShaderDX11 : public CPUTShaderDX11 { protected: ID3D11HullShader *mpHullShader; // Destructor is not public. Must release instead of delete. ~CPUTHullShaderDX11(){ SAFE_RELEASE(mpHullShader); } public: static CPUTHullShaderDX11 *CreateHullShader( const cString &name, ID3D11Device *pD3dDevice, const cString &shaderMain, const cString &shaderProfile ); static CPUTHullShaderDX11 *CreateHullShaderFromMemory( const cString &name, ID3D11Device *pD3dDevice, const cString &shaderMain, const cString &shaderProfile, const char *pShaderSource ); CPUTHullShaderDX11() : mpHullShader(NULL), CPUTShaderDX11(NULL) {} CPUTHullShaderDX11(ID3D11HullShader *pD3D11HullShader, ID3DBlob *pBlob) : mpHullShader(pD3D11HullShader), CPUTShaderDX11(pBlob) {} ID3DBlob *GetBlob() { return mpBlob; } ID3D11HullShader *GetNativeHullShader() { return mpHullShader; } }; #endif //_CPUTHULLSHADER_H ================================================ FILE: CPUT/CPUT/CPUTITTTaskMarker.cpp ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #include "CPUTITTTaskMarker.h" #ifdef CPUT_GPA_INSTRUMENTATION // Constructor // automatically creates an ITT begin marker at the start of this task //----------------------------------------------------------------------------- CPUTITTTaskMarker::CPUTITTTaskMarker(__itt_domain *pITTDomain, __itt_string_handle *pITTStringHandle) { mpITTDomain = pITTDomain; mpITTStringHandle = pITTStringHandle; __itt_task_begin(pITTDomain, __itt_null, __itt_null, pITTStringHandle); } // Destructor // When the class goes out of scope, this marker will automatically be called // and the domain 'closed' //----------------------------------------------------------------------------- CPUTITTTaskMarker::~CPUTITTTaskMarker() { __itt_task_end(mpITTDomain); } #else // This is a bit of a hack to get the compiler not to complain about this being an empty file // during the compilation in any mode that doesn't have CPUT_GPA_INSTRUMENTATION defined #define CPUTITTTaskMarkerNotEmpty() namespace { char CPUTITTTaskMarkerDummy##__LINE__; } CPUTITTTaskMarkerNotEmpty(); #endif ================================================ FILE: CPUT/CPUT/CPUTITTTaskMarker.h ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #ifndef __CPUTITTTASKMARKER_H__ #define __CPUTITTTASKMARKER_H__ #include "CPUT.h" #ifdef CPUT_GPA_INSTRUMENTATION // GPA ITT instrumentation helper class - only available in profile build // Automatically open/close marks an ITT marker event for GPA //----------------------------------------------------------------------------- class CPUTITTTaskMarker { public: CPUTITTTaskMarker(__itt_domain *pITTDomain, __itt_string_handle *pITTStringHandle); ~CPUTITTTaskMarker(); private: __itt_domain *mpITTDomain; __itt_string_handle *mpITTStringHandle; }; #endif #endif // #ifndef __CPUTITTTASKMARKER_H__ ================================================ FILE: CPUT/CPUT/CPUTInputLayoutCache.h ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #ifndef __CPUTINPUTLAYOUTMANAGER_H__ #define __CPUTINPUTLAYOUTMANAGER_H__ #include "CPUT.h" class CPUTInputLayoutCache { public: CPUTInputLayoutCache() {;} virtual ~CPUTInputLayoutCache() {;} private: }; #endif //#define __CPUTINPUTLAYOUTMANAGER_H__ ================================================ FILE: CPUT/CPUT/CPUTInputLayoutCacheDX11.cpp ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #include "CPUTInputLayoutCacheDX11.h" #include "CPUTVertexShaderDX11.h" extern const cString *gpDXGIFormatNames; CPUTInputLayoutCacheDX11* CPUTInputLayoutCacheDX11::mpInputLayoutCache = NULL; //----------------------------------------------------------------------------- void CPUTInputLayoutCacheDX11::ClearLayoutCache() { // iterate over the entire map - and release each layout object std::map::iterator mapIterator; for(mapIterator = mLayoutList.begin(); mapIterator != mLayoutList.end(); mapIterator++) { mapIterator->second->Release(); // release the ID3D11InputLayout* } mLayoutList.clear(); } // singleton retriever //----------------------------------------------------------------------------- CPUTInputLayoutCacheDX11* CPUTInputLayoutCacheDX11::GetInputLayoutCache() { if(NULL == mpInputLayoutCache) { mpInputLayoutCache = new CPUTInputLayoutCacheDX11(); } return mpInputLayoutCache; } // singleton destroy routine //----------------------------------------------------------------------------- CPUTResult CPUTInputLayoutCacheDX11::DeleteInputLayoutCache() { if(mpInputLayoutCache) { delete mpInputLayoutCache; mpInputLayoutCache = NULL; } return CPUT_SUCCESS; } // find existing, or create new, ID3D11InputLayout layout //----------------------------------------------------------------------------- CPUTResult CPUTInputLayoutCacheDX11::GetLayout( ID3D11Device *pDevice, D3D11_INPUT_ELEMENT_DESC *pDXLayout, CPUTVertexShaderDX11 *pVertexShader, ID3D11InputLayout **ppInputLayout ){ // Generate the vertex layout pattern portion of the key cString layoutKey = GenerateLayoutKey(pDXLayout); // Append the vertex shader pointer to the key for layout<->vertex shader relationship cString address = ptoc(pVertexShader); layoutKey += address; // Do we already have one like this? if( mLayoutList[layoutKey] ) { *ppInputLayout = mLayoutList[layoutKey]; (*ppInputLayout)->AddRef(); return CPUT_SUCCESS; } // Not found, create a new ID3D11InputLayout object // How many elements are in the input layout? int numInputLayoutElements=0; while(NULL != pDXLayout[numInputLayoutElements].SemanticName) { numInputLayoutElements++; } // Create the input layout HRESULT hr; ID3DBlob *pBlob = pVertexShader->GetBlob(); hr = pDevice->CreateInputLayout( pDXLayout, numInputLayoutElements, pBlob->GetBufferPointer(), pBlob->GetBufferSize(), ppInputLayout ); ASSERT( SUCCEEDED(hr), _L("Error creating input layout.") ); CPUTSetDebugName( *ppInputLayout, _L("CPUTInputLayoutCacheDX11::GetLayout()") ); // Store this layout object in our map mLayoutList[layoutKey] = *ppInputLayout; // Addref for storing it in our map as well as returning it (count should be = 2 at this point) (*ppInputLayout)->AddRef(); return CPUT_SUCCESS; } // Generate a string version of the vertex-buffer's layout. Allows us to search, compare, etc... //----------------------------------------------------------------------------- cString CPUTInputLayoutCacheDX11::GenerateLayoutKey(D3D11_INPUT_ELEMENT_DESC *pDXLayout) { // TODO: Duh! We can simply memcmp the DX layouts == use the layout input description directly as the key. // We just need to know how many elements, or NULL terminate it. // Uses less memory, faster, etc... // Duh! if( !pDXLayout[0].SemanticName ) { return _L(""); } // TODO: Use shorter names, etc... ASSERT( (pDXLayout[0].Format>=0) && (pDXLayout[0].Format<=DXGI_FORMAT_BC7_UNORM_SRGB), _L("Invalid DXGI Format.") ); // Start first layout entry and no comma. cString layoutKey = s2ws(pDXLayout[0].SemanticName) + _L(":") + gpDXGIFormatNames[pDXLayout[0].Format]; for( int index=1; NULL != pDXLayout[index].SemanticName; index++ ) { ASSERT( (pDXLayout[index].Format>=0) && (pDXLayout[index].Format<=DXGI_FORMAT_BC7_UNORM_SRGB), _L("Invalid DXGI Format.") ); // Add a comma and the next layout entry layoutKey = layoutKey + _L(",") + s2ws(pDXLayout[index].SemanticName) + _L(":") + gpDXGIFormatNames[pDXLayout[index].Format]; } return layoutKey; } ================================================ FILE: CPUT/CPUT/CPUTInputLayoutCacheDX11.h ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #ifndef __CPUTINPUTLAYOUTCACHERDX11_H__ #define __CPUTINPUTLAYOUTCACHERDX11_H__ #include "CPUTInputLayoutCache.h" #include "CPUTOSServicesWin.h" #include "CPUTVertexShaderDX11.h" #include // D3D11_INPUT_ELEMENT_DESC #include class CPUTInputLayoutCacheDX11:public CPUTInputLayoutCache { public: ~CPUTInputLayoutCacheDX11() { ClearLayoutCache(); } static CPUTInputLayoutCacheDX11 *GetInputLayoutCache(); static CPUTResult DeleteInputLayoutCache(); CPUTResult GetLayout(ID3D11Device *pDevice, D3D11_INPUT_ELEMENT_DESC *pDXLayout, CPUTVertexShaderDX11 *pVertexShader, ID3D11InputLayout **ppInputLayout); void ClearLayoutCache(); private: // singleton CPUTInputLayoutCacheDX11() { mLayoutList.clear(); } // convert the D3D11_INPUT_ELEMENT_DESC to string key cString GenerateLayoutKey(D3D11_INPUT_ELEMENT_DESC *pDXLayout); CPUTResult VerifyLayoutCompatibility(D3D11_INPUT_ELEMENT_DESC *pDXLayout, ID3DBlob *pVertexShaderBlob); static CPUTInputLayoutCacheDX11 *mpInputLayoutCache; std::map mLayoutList; }; #endif //#define __CPUTINPUTLAYOUTCACHERDX11_H__ ================================================ FILE: CPUT/CPUT/CPUTLight.cpp ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #include "CPUT.h" #include "CPUTLight.h" // Read light properties from .set file //----------------------------------------------------------------------------- CPUTResult CPUTLight::LoadLight(CPUTConfigBlock *pBlock, int *pParentID) { ASSERT( (NULL!=pBlock), _L("Invalid NULL parameter.") ); CPUTResult result = CPUT_SUCCESS; // set the null/group node name mName = pBlock->GetValueByName(_L("name"))->ValueAsString(); // get the parent ID *pParentID = pBlock->GetValueByName(_L("parent"))->ValueAsInt(); LoadParentMatrixFromParameterBlock( pBlock ); cString lightType = pBlock->GetValueByName(_L("lighttype"))->ValueAsString(); if(lightType.compare(_L("spot")) == 0) { mLightParams.nLightType = CPUT_LIGHT_SPOT; } else if(lightType.compare(_L("directional")) == 0) { mLightParams.nLightType = CPUT_LIGHT_DIRECTIONAL; } else if(lightType.compare(_L("point")) == 0) { mLightParams.nLightType = CPUT_LIGHT_POINT; } else { // ASSERT(0,_L("")); // TODO: why doesn't assert work here? } pBlock->GetValueByName(_L("Color"))->ValueAsFloatArray(mLightParams.pColor, 3); mLightParams.fIntensity = pBlock->GetValueByName(_L("Intensity"))->ValueAsFloat(); mLightParams.fHotSpot = pBlock->GetValueByName(_L("HotSpot"))->ValueAsFloat(); mLightParams.fConeAngle = pBlock->GetValueByName(_L("ConeAngle"))->ValueAsFloat(); mLightParams.fDecayStart = pBlock->GetValueByName(_L("DecayStart"))->ValueAsFloat(); mLightParams.bEnableFarAttenuation = pBlock->GetValueByName(_L("EnableNearAttenuation"))->ValueAsBool(); mLightParams.bEnableFarAttenuation = pBlock->GetValueByName(_L("EnableFarAttenuation"))->ValueAsBool(); mLightParams.fNearAttenuationStart = pBlock->GetValueByName(_L("NearAttenuationStart"))->ValueAsFloat(); mLightParams.fNearAttenuationEnd = pBlock->GetValueByName(_L("NearAttenuationEnd"))->ValueAsFloat(); mLightParams.fFarAttenuationStart = pBlock->GetValueByName(_L("FarAttenuationStart"))->ValueAsFloat(); mLightParams.fFarAttenuationEnd = pBlock->GetValueByName(_L("FarAttenuationEnd"))->ValueAsFloat(); return result; } ================================================ FILE: CPUT/CPUT/CPUTLight.h ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #ifndef __CPUTLight_H__ #define __CPUTLight_H__ #include "CPUT.h" #include "CPUTRenderNode.h" #include "CPUTConfigBlock.h" enum LightType { CPUT_LIGHT_DIRECTIONAL, CPUT_LIGHT_POINT, CPUT_LIGHT_SPOT, }; struct CPUTLightParams { LightType nLightType; float pColor[3]; float fIntensity; float fHotSpot; float fConeAngle; float fDecayStart; bool bEnableNearAttenuation; bool bEnableFarAttenuation; float fNearAttenuationStart; float fNearAttenuationEnd; float fFarAttenuationStart; float fFarAttenuationEnd; }; class CPUTLight:public CPUTRenderNode { protected: CPUTLightParams mLightParams; public: CPUTLight() {} virtual ~CPUTLight() {} void SetLightParameters(CPUTLightParams &lightParams); CPUTLightParams *GetLightParameters() {return &mLightParams; } CPUTResult LoadLight(CPUTConfigBlock *pBlock, int *pParentID); }; #endif //#ifndef __CPUTLight_H__ ================================================ FILE: CPUT/CPUT/CPUTMaterial.cpp ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #include "CPUT.h" #include "CPUTAssetLibrary.h" #ifdef CPUT_FOR_DX11 #include "CPUTMaterialDX11.h" #else #error You must supply a target graphics API (ex: #define CPUT_FOR_DX11), or implement the target API for this file. #endif CPUTMaterial *CPUTMaterial::CreateMaterial( const cString &absolutePathAndFilename, const cString &modelSuffix, const cString &meshSuffix ) { // material was not in the library, so load it #ifdef CPUT_FOR_DX11 CPUTMaterial *pMaterial = new CPUTMaterialDX11(); #else #error You must supply a target graphics API (ex: #define CPUT_FOR_DX11), or implement the target API for this file. #endif CPUTResult result = pMaterial->LoadMaterial(absolutePathAndFilename, modelSuffix, meshSuffix); ASSERT( CPUTSUCCESS(result), _L("\nError - CPUTAssetLibrary::GetMaterial() - Error in material file: '")+absolutePathAndFilename+_L("'") ); // add material to material library list // cString finalName = pMaterial->MaterialRequiresPerModelPayload() ? absolutePathAndFilename + modelSuffix + meshSuffix : absolutePathAndFilename; // CPUTAssetLibrary::GetAssetLibrary()->AddMaterial( finalName, pMaterial ); CPUTAssetLibrary::GetAssetLibrary()->AddMaterial( absolutePathAndFilename, pMaterial ); return pMaterial; } ================================================ FILE: CPUT/CPUT/CPUTMaterial.h ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #ifndef __CPUTMATERIAL_H__ #define __CPUTMATERIAL_H__ #include #include "CPUT.h" #include "CPUTRefCount.h" #include "CPUTConfigBlock.h" #include "CPUTTexture.h" #include "CPUTRenderStateBlock.h" class CPUTShaderParameters; // TODO: Where did this number come frome? It should also be different for each API #define CPUT_MATERIAL_MAX_TEXTURE_SLOTS 32 #define CPUT_MATERIAL_MAX_BUFFER_SLOTS 32 #define CPUT_MATERIAL_MAX_CONSTANT_BUFFER_SLOTS 32 #define CPUT_MATERIAL_MAX_SRV_SLOTS 32 #if 0 // Need to handle >=DX11 vs. < DX11, where max UAV slots == 1; # define CPUT_MATERIAL_MAX_UAV_SLOTS 7 #else # define CPUT_MATERIAL_MAX_UAV_SLOTS 1 #endif class CPUTMaterial:public CPUTRefCount { protected: cString mMaterialName; CPUTConfigBlock mConfigBlock; CPUTRenderStateBlock *mpRenderStateBlock; UINT mBufferCount; CPUTTexture *mpTexture[CPUT_MATERIAL_MAX_TEXTURE_SLOTS]; // TODO: DX11 has buffers, UAVs, and constant buffers. Do these belong there? CPUTBuffer *mpBuffer[CPUT_MATERIAL_MAX_BUFFER_SLOTS]; CPUTBuffer *mpUAV[CPUT_MATERIAL_MAX_UAV_SLOTS]; CPUTBuffer *mpConstantBuffer[CPUT_MATERIAL_MAX_CONSTANT_BUFFER_SLOTS]; // Destructor is not public. Must release instead of delete. virtual ~CPUTMaterial(){ // The following are allocated in the derived class. So, release there too. // for( UINT ii=0; iiSetRenderStates(renderParams); } } virtual bool MaterialRequiresPerModelPayload() = 0; virtual CPUTMaterial *CloneMaterial( const cString &absolutePathAndFilename, const cString &modelSuffix, const cString &meshSuffix ) = 0; }; #endif //#ifndef __CPUTMATERIAL_H__ ================================================ FILE: CPUT/CPUT/CPUTMaterialDX11.cpp ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #include "CPUTMaterialDX11.h" #include "CPUT_DX11.h" #include "CPUTTextureDX11.h" #include "CPUTBufferDX11.h" #include "CPUTVertexShaderDX11.h" #include "CPUTPixelShaderDX11.h" #include "CPUTComputeShaderDX11.h" #include "CPUTGeometryShaderDX11.h" #include "CPUTDomainShaderDX11.h" #include "CPUTHullShaderDX11.h" #define OUTPUT_BINDING_DEBUG_INFO(x) // #define OUTPUT_BINDING_DEBUG_INFO(x) OutputDebugString(x) CPUTConfigBlock CPUTMaterial::mGlobalProperties; // Note: These initial values shouldn't really matter. We call ResetStateTracking() before we render (and it performs these initializations) void *CPUTMaterialDX11::mpLastVertexShader = (void*)-1; void *CPUTMaterialDX11::mpLastPixelShader = (void*)-1; void *CPUTMaterialDX11::mpLastComputeShader = (void*)-1; void *CPUTMaterialDX11::mpLastGeometryShader = (void*)-1; void *CPUTMaterialDX11::mpLastHullShader = (void*)-1; void *CPUTMaterialDX11::mpLastDomainShader = (void*)-1; void *CPUTMaterialDX11::mpLastVertexShaderViews[CPUT_MATERIAL_MAX_TEXTURE_SLOTS] = {0}; void *CPUTMaterialDX11::mpLastPixelShaderViews[CPUT_MATERIAL_MAX_TEXTURE_SLOTS] = {0}; void *CPUTMaterialDX11::mpLastComputeShaderViews[CPUT_MATERIAL_MAX_TEXTURE_SLOTS] = {0}; void *CPUTMaterialDX11::mpLastGeometryShaderViews[CPUT_MATERIAL_MAX_TEXTURE_SLOTS] = {0}; void *CPUTMaterialDX11::mpLastHullShaderViews[CPUT_MATERIAL_MAX_TEXTURE_SLOTS] = {0}; void *CPUTMaterialDX11::mpLastDomainShaderViews[CPUT_MATERIAL_MAX_TEXTURE_SLOTS] = {0}; void *CPUTMaterialDX11::mpLastVertexShaderConstantBuffers[CPUT_MATERIAL_MAX_CONSTANT_BUFFER_SLOTS] = {0}; void *CPUTMaterialDX11::mpLastPixelShaderConstantBuffers[CPUT_MATERIAL_MAX_CONSTANT_BUFFER_SLOTS] = {0}; void *CPUTMaterialDX11::mpLastComputeShaderConstantBuffers[CPUT_MATERIAL_MAX_CONSTANT_BUFFER_SLOTS] = {0}; void *CPUTMaterialDX11::mpLastGeometryShaderConstantBuffers[CPUT_MATERIAL_MAX_CONSTANT_BUFFER_SLOTS] = {0}; void *CPUTMaterialDX11::mpLastHullShaderConstantBuffers[CPUT_MATERIAL_MAX_CONSTANT_BUFFER_SLOTS] = {0}; void *CPUTMaterialDX11::mpLastDomainShaderConstantBuffers[CPUT_MATERIAL_MAX_CONSTANT_BUFFER_SLOTS] = {0}; void *CPUTMaterialDX11::mpLastComputeShaderUAVs[CPUT_MATERIAL_MAX_UAV_SLOTS] = {0}; void *CPUTMaterialDX11::mpLastRenderStateBlock = (void*)-1; //----------------------------------------------------------------------------- CPUTShaderParameters::~CPUTShaderParameters() { for(int ii=0; ii##SHADER_TYPE##SetShader( mp##SHADER##Shader ? mp##SHADER##Shader->GetNative##SHADER##Shader() : NULL, NULL, 0 ); \ } \ /* Spend time checking shader resources only if a shader is bound ... */ \ if( mp##SHADER##Shader ) \ { \ if( m##SHADER##ShaderParameters.mTextureCount || m##SHADER##ShaderParameters.mBufferCount ) \ { \ same = true; \ /* If all of the texture slots we need are already bound to our textures, then skip setting the SRVs... */\ for( UINT ii=0; ii < m##SHADER##ShaderParameters.mTextureCount; ii++ ) \ { \ UINT bindPoint = m##SHADER##ShaderParameters.mpTextureParameterBindPoint[ii]; \ if(mpLast##SHADER##ShaderViews[bindPoint] != m##SHADER##ShaderParameters.mppBindViews[bindPoint] ) \ { \ mpLast##SHADER##ShaderViews[bindPoint] = m##SHADER##ShaderParameters.mppBindViews[bindPoint]; \ same = false; \ } \ } \ for( UINT ii=0; ii < m##SHADER##ShaderParameters.mBufferCount; ii++ ) \ { \ UINT bindPoint = m##SHADER##ShaderParameters.mpBufferParameterBindPoint[ii]; \ if(mpLast##SHADER##ShaderViews[bindPoint] != m##SHADER##ShaderParameters.mppBindViews[bindPoint] ) \ { \ mpLast##SHADER##ShaderViews[bindPoint] = m##SHADER##ShaderParameters.mppBindViews[bindPoint]; \ same = false; \ } \ } \ if( !same ) \ { \ int min = m##SHADER##ShaderParameters.mBindViewMin; \ int max = m##SHADER##ShaderParameters.mBindViewMax; \ int count = max - min + 1; \ pContext->SHADER_TYPE##SetShaderResources( min, count, &m##SHADER##ShaderParameters.mppBindViews[min] ); \ } \ } \ if( m##SHADER##ShaderParameters.mConstantBufferCount ) \ { \ same = true; \ /* If all of the constant buffer slots we need are already bound to our constant buffers, then skip setting the SRVs... */\ for( UINT ii=0; iiSHADER_TYPE##SetConstantBuffers(min, count, &m##SHADER##ShaderParameters.mppBindConstantBuffers[min] ); \ } \ } \ } //----------------------------------------------------------------------------- void CPUTMaterialDX11::SetRenderStates( CPUTRenderParameters &renderParams ) { ID3D11DeviceContext *pContext = ((CPUTRenderParametersDX*)&renderParams)->mpContext; bool same = true; SET_SHADER_RESOURCES( Vertex, VS ); SET_SHADER_RESOURCES( Pixel, PS ); SET_SHADER_RESOURCES( Compute, CS ); SET_SHADER_RESOURCES( Geometry, GS ); SET_SHADER_RESOURCES( Hull, HS ); SET_SHADER_RESOURCES( Domain, DS ); // Only the compute shader may have UAVs to bind. // Note that pixel shaders can too, but DX requires setting those when setting RTV(s). same = true; for( UINT ii=0; iiOMGetRenderTargets( D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT, &pRTVs[0], &pDSV ); // Count the RTVs UINT rtvCount; for( rtvCount=D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT-1; rtvCount>0; rtvCount-- ) { if( 0 != pRTVs[rtvCount] ) break; } rtvCount+=1; pContext->OMSetRenderTargetsAndUnorderedAccessViews( rtvCount, pRTVs, pDSV, rtvCount, CPUT_MATERIAL_MAX_UAV_SLOTS-rtvCount, &mPixelShaderParameters.mppBindUAVs[rtvCount], NULL ); SAFE_RELEASE(pDSV); for( UINT ii=0; iiCSSetUnorderedAccessViews(min, count, &mComputeShaderParameters.mppBindUAVs[min], NULL ); } // Set the render state block if it changed if( mpLastRenderStateBlock != mpRenderStateBlock ) { mpLastRenderStateBlock = mpRenderStateBlock; if( mpRenderStateBlock ) { // We know we have a DX11 class. Does this correctly bypass the virtual? // Should we move it to the DX11 class. ((CPUTRenderStateBlockDX11*)mpRenderStateBlock)->SetRenderStates(renderParams); } else { CPUTRenderStateBlock::GetDefaultRenderStateBlock()->SetRenderStates(renderParams); } } } //----------------------------------------------------------------------------- void CPUTMaterialDX11::ReadShaderSamplersAndTextures( ID3DBlob *pBlob, CPUTShaderParameters *pShaderParameter ) { // *************************** // Use shader reflection to get texture and sampler names. We use them later to bind .mtl texture-specification to shader parameters/variables. // TODO: Currently do this only for PS. Do for other shader types too. // TODO: Generalize, so easy to call for different shader types // *************************** ID3D11ShaderReflection *pReflector = NULL; D3D11_SHADER_INPUT_BIND_DESC desc; D3DReflect( pBlob->GetBufferPointer(), pBlob->GetBufferSize(), IID_ID3D11ShaderReflection, (void**)&pReflector); // Walk through the shader input bind descriptors. Find the samplers and textures. int ii=0; HRESULT hr = pReflector->GetResourceBindingDesc( ii++, &desc ); while( SUCCEEDED(hr) ) { switch( desc.Type ) { case D3D_SIT_TEXTURE: pShaderParameter->mTextureParameterCount++; break; case D3D_SIT_SAMPLER: pShaderParameter->mSamplerParameterCount++; break; case D3D_SIT_CBUFFER: pShaderParameter->mConstantBufferParameterCount++; break; case D3D_SIT_TBUFFER: case D3D_SIT_STRUCTURED: case D3D_SIT_BYTEADDRESS: pShaderParameter->mBufferParameterCount++; break; case D3D_SIT_UAV_RWTYPED: case D3D_SIT_UAV_RWSTRUCTURED: case D3D_SIT_UAV_RWBYTEADDRESS: case D3D_SIT_UAV_APPEND_STRUCTURED: case D3D_SIT_UAV_CONSUME_STRUCTURED: case D3D_SIT_UAV_RWSTRUCTURED_WITH_COUNTER: pShaderParameter->mUAVParameterCount++; break; } hr = pReflector->GetResourceBindingDesc( ii++, &desc ); } pShaderParameter->mpTextureParameterName = new cString[pShaderParameter->mTextureParameterCount]; pShaderParameter->mpTextureParameterBindPoint = new UINT[ pShaderParameter->mTextureParameterCount]; pShaderParameter->mpSamplerParameterName = new cString[pShaderParameter->mSamplerParameterCount]; pShaderParameter->mpSamplerParameterBindPoint = new UINT[ pShaderParameter->mSamplerParameterCount]; pShaderParameter->mpBufferParameterName = new cString[pShaderParameter->mBufferParameterCount]; pShaderParameter->mpBufferParameterBindPoint = new UINT[ pShaderParameter->mBufferParameterCount]; pShaderParameter->mpUAVParameterName = new cString[pShaderParameter->mUAVParameterCount]; pShaderParameter->mpUAVParameterBindPoint = new UINT[ pShaderParameter->mUAVParameterCount]; pShaderParameter->mpConstantBufferParameterName = new cString[pShaderParameter->mConstantBufferParameterCount]; pShaderParameter->mpConstantBufferParameterBindPoint = new UINT[ pShaderParameter->mConstantBufferParameterCount]; // Start over. This time, copy the names. ii=0; UINT textureIndex = 0; UINT samplerIndex = 0; UINT bufferIndex = 0; UINT uavIndex = 0; UINT constantBufferIndex = 0; hr = pReflector->GetResourceBindingDesc( ii++, &desc ); while( SUCCEEDED(hr) ) { switch( desc.Type ) { case D3D_SIT_TEXTURE: pShaderParameter->mpTextureParameterName[textureIndex] = s2ws(desc.Name); pShaderParameter->mpTextureParameterBindPoint[textureIndex] = desc.BindPoint; textureIndex++; break; case D3D_SIT_SAMPLER: pShaderParameter->mpSamplerParameterName[samplerIndex] = s2ws(desc.Name); pShaderParameter->mpSamplerParameterBindPoint[samplerIndex] = desc.BindPoint; samplerIndex++; break; case D3D_SIT_CBUFFER: pShaderParameter->mpConstantBufferParameterName[constantBufferIndex] = s2ws(desc.Name); pShaderParameter->mpConstantBufferParameterBindPoint[constantBufferIndex] = desc.BindPoint; constantBufferIndex++; break; case D3D_SIT_TBUFFER: case D3D_SIT_STRUCTURED: case D3D_SIT_BYTEADDRESS: pShaderParameter->mpBufferParameterName[bufferIndex] = s2ws(desc.Name); pShaderParameter->mpBufferParameterBindPoint[bufferIndex] = desc.BindPoint; bufferIndex++; break; case D3D_SIT_UAV_RWTYPED: case D3D_SIT_UAV_RWSTRUCTURED: case D3D_SIT_UAV_RWBYTEADDRESS: case D3D_SIT_UAV_APPEND_STRUCTURED: case D3D_SIT_UAV_CONSUME_STRUCTURED: case D3D_SIT_UAV_RWSTRUCTURED_WITH_COUNTER: pShaderParameter->mpUAVParameterName[uavIndex] = s2ws(desc.Name); pShaderParameter->mpUAVParameterBindPoint[uavIndex] = desc.BindPoint; uavIndex++; break; } hr = pReflector->GetResourceBindingDesc( ii++, &desc ); } } //----------------------------------------------------------------------------- void CPUTMaterialDX11::BindTextures( CPUTShaderParameters ¶ms, const cString &modelSuffix, const cString &meshSuffix ) { CPUTAssetLibraryDX11 *pAssetLibrary = (CPUTAssetLibraryDX11*)CPUTAssetLibrary::GetAssetLibrary(); for(params.mTextureCount=0; params.mTextureCount < params.mTextureParameterCount; params.mTextureCount++) { cString textureName; UINT textureCount = params.mTextureCount; cString tagName = params.mpTextureParameterName[textureCount]; CPUTConfigEntry *pValue = mConfigBlock.GetValueByName(tagName); if( !pValue->IsValid() ) { // We didn't find our property in the file. Is it in the global config block? pValue = mGlobalProperties.GetValueByName(tagName); } ASSERT( pValue->IsValid(), L"Can't find texture '" + tagName + L"'." ); // TODO: fix message textureName = pValue->ValueAsString(); // If the texture name not specified. Load default.dds instead if( 0 == textureName.length() ) { textureName = _L("default.dds"); } UINT bindPoint = params.mpTextureParameterBindPoint[textureCount]; ASSERT( bindPoint < CPUT_MATERIAL_MAX_TEXTURE_SLOTS, _L("Texture bind point out of range.") ); params.mBindViewMin = min( params.mBindViewMin, bindPoint ); params.mBindViewMax = max( params.mBindViewMax, bindPoint ); if( textureName[0] == '@' ) { // This is a per-mesh value. Add to per-mesh list. textureName += modelSuffix + meshSuffix; } else if( textureName[0] == '#' ) { // This is a per-mesh value. Add to per-mesh list. textureName += modelSuffix; } // Get the sRGB flag (default to true) cString SRGBName = tagName+_L("sRGB"); CPUTConfigEntry *pSRGBValue = mConfigBlock.GetValueByName(SRGBName); bool loadAsSRGB = pSRGBValue->IsValid() ? loadAsSRGB = pSRGBValue->ValueAsBool() : true; if( !params.mpTexture[textureCount] ) { params.mpTexture[textureCount] = pAssetLibrary->GetTexture( textureName, false, loadAsSRGB ); ASSERT( params.mpTexture[textureCount], _L("Failed getting texture ") + textureName); } // The shader file (e.g. .fx) can specify the texture bind point (e.g., t0). Those specifications // might not be contiguous, and there might be gaps (bind points without assigned textures) // TODO: Warn about missing bind points? params.mppBindViews[bindPoint] = ((CPUTTextureDX11*)params.mpTexture[textureCount])->GetShaderResourceView(); params.mppBindViews[bindPoint]->AddRef(); OUTPUT_BINDING_DEBUG_INFO( (itoc(bindPoint) + _L(" : ") + params.mpTexture[textureCount]->GetName() + _L("\n")).c_str() ); } } //----------------------------------------------------------------------------- void CPUTMaterialDX11::BindBuffers( CPUTShaderParameters ¶ms, const cString &modelSuffix, const cString &meshSuffix ) { CPUTConfigEntry *pValue; if( !params.mBufferParameterCount ) { return; } OUTPUT_BINDING_DEBUG_INFO( _L("Bound Buffers") ); CPUTAssetLibraryDX11 *pAssetLibrary = (CPUTAssetLibraryDX11*)CPUTAssetLibrary::GetAssetLibrary(); for(params.mBufferCount=0; params.mBufferCount < params.mBufferParameterCount; params.mBufferCount++) { cString bufferName; UINT bufferCount = params.mBufferCount; cString tagName = params.mpBufferParameterName[bufferCount]; { pValue = mConfigBlock.GetValueByName(tagName); if( !pValue->IsValid() ) { // We didn't find our property in the file. Is it in the global config block? pValue = mGlobalProperties.GetValueByName(tagName); } ASSERT( pValue->IsValid(), L"Can't find buffer '" + tagName + L"'." ); // TODO: fix message bufferName = pValue->ValueAsString(); } UINT bindPoint = params.mpBufferParameterBindPoint[bufferCount]; ASSERT( bindPoint < CPUT_MATERIAL_MAX_BUFFER_SLOTS, _L("Buffer bind point out of range.") ); params.mBindViewMin = min( params.mBindViewMin, bindPoint ); params.mBindViewMax = max( params.mBindViewMax, bindPoint ); if( bufferName[0] == '@' ) { // This is a per-mesh value. Add to per-mesh list. bufferName += modelSuffix + meshSuffix; } else if( bufferName[0] == '#' ) { // This is a per-mesh value. Add to per-model list. bufferName += modelSuffix; } if( !params.mpBuffer[bufferCount] ) { params.mpBuffer[bufferCount] = pAssetLibrary->GetBuffer( bufferName ); ASSERT( params.mpBuffer[bufferCount], _L("Failed getting buffer ") + bufferName); } params.mppBindViews[bindPoint] = ((CPUTBufferDX11*)params.mpBuffer[bufferCount])->GetShaderResourceView(); if( params.mppBindViews[bindPoint] ) { params.mppBindViews[bindPoint]->AddRef();} OUTPUT_BINDING_DEBUG_INFO( (itoc(bindPoint) + _L(" : ") + params.mpBuffer[bufferCount]->GetName() + _L("\n")).c_str() ); } OUTPUT_BINDING_DEBUG_INFO( _L("\n") ); } //----------------------------------------------------------------------------- void CPUTMaterialDX11::BindUAVs( CPUTShaderParameters ¶ms, const cString &modelSuffix, const cString &meshSuffix ) { CPUTConfigEntry *pValue; if( !params.mUAVParameterCount ) { return; } OUTPUT_BINDING_DEBUG_INFO( _L("Bound UAVs") ); CPUTAssetLibraryDX11 *pAssetLibrary = (CPUTAssetLibraryDX11*)CPUTAssetLibrary::GetAssetLibrary(); memset( params.mppBindUAVs, 0, sizeof(params.mppBindUAVs) ); for(params.mUAVCount=0; params.mUAVCount < params.mUAVParameterCount; params.mUAVCount++) { cString uavName; UINT uavCount = params.mUAVCount; cString tagName = params.mpUAVParameterName[uavCount]; { pValue = mConfigBlock.GetValueByName(tagName); if( !pValue->IsValid() ) { // We didn't find our property in the file. Is it in the global config block? pValue = mGlobalProperties.GetValueByName(tagName); } ASSERT( pValue->IsValid(), L"Can't find UAV '" + tagName + L"'." ); // TODO: fix message uavName = pValue->ValueAsString(); } UINT bindPoint = params.mpUAVParameterBindPoint[uavCount]; ASSERT( bindPoint < CPUT_MATERIAL_MAX_UAV_SLOTS, _L("UAV bind point out of range.") ); params.mBindUAVMin = min( params.mBindUAVMin, bindPoint ); params.mBindUAVMax = max( params.mBindUAVMax, bindPoint ); if( uavName[0] == '@' ) { // This is a per-mesh value. Add to per-mesh list. uavName += modelSuffix + meshSuffix; } else if( uavName[0] == '#' ) { // This is a per-mesh value. Add to per-model list. uavName += modelSuffix; } if( !params.mpUAV[uavCount] ) { params.mpUAV[uavCount] = pAssetLibrary->GetBuffer( uavName ); ASSERT( params.mpUAV[uavCount], _L("Failed getting UAV ") + uavName); } // If has UAV, then add to mppBindUAV params.mppBindUAVs[bindPoint] = ((CPUTBufferDX11*)params.mpUAV[uavCount])->GetUnorderedAccessView(); if( params.mppBindUAVs[bindPoint] ) { params.mppBindUAVs[bindPoint]->AddRef();} OUTPUT_BINDING_DEBUG_INFO( (itoc(bindPoint) + _L(" : ") + params.mpUAV[uavCount]->GetName() + _L("\n")).c_str() ); } OUTPUT_BINDING_DEBUG_INFO( _L("\n") ); } //----------------------------------------------------------------------------- void CPUTMaterialDX11::BindConstantBuffers( CPUTShaderParameters ¶ms, const cString &modelSuffix, const cString &meshSuffix ) { CPUTConfigEntry *pValue; if( !params.mConstantBufferParameterCount ) { return; } OUTPUT_BINDING_DEBUG_INFO( _L("Bound Constant Buffers\n") ); CPUTAssetLibraryDX11 *pAssetLibrary = (CPUTAssetLibraryDX11*)CPUTAssetLibrary::GetAssetLibrary(); for(params.mConstantBufferCount=0; params.mConstantBufferCount < params.mConstantBufferParameterCount; params.mConstantBufferCount++) { cString constantBufferName; UINT constantBufferCount = params.mConstantBufferCount; cString tagName = params.mpConstantBufferParameterName[constantBufferCount]; { pValue = mConfigBlock.GetValueByName(tagName); if( !pValue->IsValid() ) { // We didn't find our property in the file. Is it in the global config block? pValue = mGlobalProperties.GetValueByName(tagName); } ASSERT( pValue->IsValid(), L"Can't find constant buffer '" + tagName + L"'." ); // TODO: fix message constantBufferName = pValue->ValueAsString(); } UINT bindPoint = params.mpConstantBufferParameterBindPoint[constantBufferCount]; ASSERT( bindPoint < CPUT_MATERIAL_MAX_CONSTANT_BUFFER_SLOTS, _L("Constant buffer bind point out of range.") ); params.mBindConstantBufferMin = min( params.mBindConstantBufferMin, bindPoint ); params.mBindConstantBufferMax = max( params.mBindConstantBufferMax, bindPoint ); if( constantBufferName[0] == '@' ) { constantBufferName += modelSuffix + meshSuffix; } else if( constantBufferName[0] == '#' ) { constantBufferName += modelSuffix; } if( !params.mpConstantBuffer[constantBufferCount] ) { params.mpConstantBuffer[constantBufferCount] = pAssetLibrary->GetConstantBuffer( constantBufferName ); ASSERT( params.mpConstantBuffer[constantBufferCount], _L("Failed getting constant buffer ") + constantBufferName); } // If has constant buffer, then add to mppBindConstantBuffer params.mppBindConstantBuffers[bindPoint] = ((CPUTBufferDX11*)params.mpConstantBuffer[constantBufferCount])->GetNativeBuffer(); if( params.mppBindConstantBuffers[bindPoint] ) { params.mppBindConstantBuffers[bindPoint]->AddRef();} OUTPUT_BINDING_DEBUG_INFO( (itoc(bindPoint) + _L(" : ") + params.mpConstantBuffer[constantBufferCount]->GetName() + _L("\n")).c_str() ); } OUTPUT_BINDING_DEBUG_INFO( _L("\n") ); } //----------------------------------------------------------------------------- CPUTResult CPUTMaterialDX11::LoadMaterial(const cString &fileName, const cString &modelSuffix, const cString &meshSuffix) { CPUTResult result = CPUT_SUCCESS; mMaterialName = fileName; // Open/parse the file CPUTConfigFile file; result = file.LoadFile(fileName); if(CPUTFAILED(result)) { return result; } // Make a local copy of all the parameters CPUTConfigBlock *pBlock = file.GetBlock(0); ASSERT( pBlock, _L("Error getting parameter block") ); mConfigBlock = *pBlock; // get necessary device and AssetLibrary pointers ID3D11Device *pD3dDevice = CPUT_DX11::GetDevice(); CPUTAssetLibraryDX11 *pAssetLibrary = (CPUTAssetLibraryDX11*)CPUTAssetLibrary::GetAssetLibrary(); // TODO: The following code is very repetitive. Consider generalizing so we can call a function instead. // see if there are any pixel/vertex/geo shaders to load CPUTConfigEntry *pValue, *pEntryPointName, *pProfileName; pValue = mConfigBlock.GetValueByName(_L("VertexShaderFile")); if( pValue->IsValid() ) { pEntryPointName = mConfigBlock.GetValueByName(_L("VertexShaderMain")); pProfileName = mConfigBlock.GetValueByName(_L("VertexShaderProfile")); pAssetLibrary->GetVertexShader(pValue->ValueAsString(), pD3dDevice, pEntryPointName->ValueAsString(), pProfileName->ValueAsString(), &mpVertexShader ); ReadShaderSamplersAndTextures( mpVertexShader->GetBlob(), &mVertexShaderParameters ); } // load and store the pixel shader if it was specified pValue = mConfigBlock.GetValueByName(_L("PixelShaderFile")); if( pValue->IsValid() ) { pEntryPointName = mConfigBlock.GetValueByName(_L("PixelShaderMain")); pProfileName = mConfigBlock.GetValueByName(_L("PixelShaderProfile")); pAssetLibrary->GetPixelShader(pValue->ValueAsString(), pD3dDevice, pEntryPointName->ValueAsString(), pProfileName->ValueAsString(), &mpPixelShader); ReadShaderSamplersAndTextures( mpPixelShader->GetBlob(), &mPixelShaderParameters ); } // load and store the compute shader if it was specified pValue = mConfigBlock.GetValueByName(_L("ComputeShaderFile")); if( pValue->IsValid() ) { pEntryPointName = mConfigBlock.GetValueByName(_L("ComputeShaderMain")); pProfileName = mConfigBlock.GetValueByName(_L("ComputeShaderProfile")); pAssetLibrary->GetComputeShader(pValue->ValueAsString(), pD3dDevice, pEntryPointName->ValueAsString(), pProfileName->ValueAsString(), &mpComputeShader); ReadShaderSamplersAndTextures( mpComputeShader->GetBlob(), &mComputeShaderParameters ); } // load and store the geometry shader if it was specified pValue = mConfigBlock.GetValueByName(_L("GeometryShaderFile")); if( pValue->IsValid() ) { pEntryPointName = mConfigBlock.GetValueByName(_L("GeometryShaderMain")); pProfileName = mConfigBlock.GetValueByName(_L("GeometryShaderProfile")); pAssetLibrary->GetGeometryShader(pValue->ValueAsString(), pD3dDevice, pEntryPointName->ValueAsString(), pProfileName->ValueAsString(), &mpGeometryShader); ReadShaderSamplersAndTextures( mpGeometryShader->GetBlob(), &mGeometryShaderParameters ); } // load and store the hull shader if it was specified pValue = mConfigBlock.GetValueByName(_L("HullShaderFile")); if( pValue->IsValid() ) { pEntryPointName = mConfigBlock.GetValueByName(_L("HullShaderMain")); pProfileName = mConfigBlock.GetValueByName(_L("HullShaderProfile")); pAssetLibrary->GetHullShader(pValue->ValueAsString(), pD3dDevice, pEntryPointName->ValueAsString(), pProfileName->ValueAsString(), &mpHullShader); ReadShaderSamplersAndTextures( mpHullShader->GetBlob(), &mHullShaderParameters ); } // load and store the domain shader if it was specified pValue = mConfigBlock.GetValueByName(_L("DomainShaderFile")); if( pValue->IsValid() ) { pEntryPointName = mConfigBlock.GetValueByName(_L("DomainShaderMain")); pProfileName = mConfigBlock.GetValueByName(_L("DomainShaderProfile")); pAssetLibrary->GetDomainShader(pValue->ValueAsString(), pD3dDevice, pEntryPointName->ValueAsString(), pProfileName->ValueAsString(), &mpDomainShader); ReadShaderSamplersAndTextures( mpDomainShader->GetBlob(), &mDomainShaderParameters ); } // load and store the render state file if it was specified pValue = mConfigBlock.GetValueByName(_L("RenderStateFile")); if( pValue->IsValid() ) { mpRenderStateBlock = pAssetLibrary->GetRenderStateBlock(pValue->ValueAsString()); } OUTPUT_BINDING_DEBUG_INFO( (_L("Bindings for : ") + mMaterialName + _L("\n")).c_str() ); cString pShaderTypeNameList[] = { _L("Pixel shader"), _L("Compute shader"), _L("Vertex shader"), _L("Geometry shader"), _L("Hull shader"), _L("Domain shader"), }; cString *pShaderTypeName = pShaderTypeNameList; void *pShaderList[] = { mpPixelShader, mpComputeShader, mpVertexShader, mpGeometryShader, mpHullShader, mpDomainShader }; void **pShader = pShaderList; // For each of the shader stages, bind shaders and buffers for( CPUTShaderParameters **pCur = mpShaderParametersList; *pCur; pCur++ ) // Bind textures and buffersfor each shader stage { if( !*pShader++ ) { pShaderTypeName++; // Increment the name pointer to remain coherent. continue; // This shader not bound. Don't waste time binding to it. } OUTPUT_BINDING_DEBUG_INFO( (*(pShaderTypeName++) + _L("\n")).c_str() ); BindTextures( **pCur, modelSuffix, meshSuffix ); BindBuffers( **pCur, modelSuffix, meshSuffix ); BindUAVs( **pCur, modelSuffix, meshSuffix ); BindConstantBuffers( **pCur, modelSuffix, meshSuffix ); OUTPUT_BINDING_DEBUG_INFO( _L("\n") ); } return result; } //----------------------------------------------------------------------------- CPUTMaterial *CPUTMaterialDX11::CloneMaterial(const cString &fileName, const cString &modelSuffix, const cString &meshSuffix) { CPUTMaterialDX11 *pMaterial = new CPUTMaterialDX11(); // TODO: move texture to base class. All APIs have textures. pMaterial->mpPixelShader = mpPixelShader; if(mpPixelShader) mpPixelShader->AddRef(); pMaterial->mpComputeShader = mpComputeShader; if(mpComputeShader) mpComputeShader->AddRef(); pMaterial->mpVertexShader = mpVertexShader; if(mpVertexShader) mpVertexShader->AddRef(); pMaterial->mpGeometryShader = mpGeometryShader; if(mpGeometryShader) mpGeometryShader->AddRef(); pMaterial->mpHullShader = mpHullShader; if(mpHullShader) mpHullShader->AddRef(); pMaterial->mpDomainShader = mpDomainShader; if(mpDomainShader) mpDomainShader->AddRef(); mPixelShaderParameters.CloneShaderParameters( &pMaterial->mPixelShaderParameters ); mComputeShaderParameters.CloneShaderParameters( &pMaterial->mComputeShaderParameters ); mVertexShaderParameters.CloneShaderParameters( &pMaterial->mVertexShaderParameters ); mGeometryShaderParameters.CloneShaderParameters( &pMaterial->mGeometryShaderParameters ); mHullShaderParameters.CloneShaderParameters( &pMaterial->mHullShaderParameters ); mDomainShaderParameters.CloneShaderParameters( &pMaterial->mDomainShaderParameters ); pMaterial->mpShaderParametersList[0] = &pMaterial->mPixelShaderParameters, pMaterial->mpShaderParametersList[1] = &pMaterial->mComputeShaderParameters, pMaterial->mpShaderParametersList[2] = &pMaterial->mVertexShaderParameters, pMaterial->mpShaderParametersList[3] = &pMaterial->mGeometryShaderParameters, pMaterial->mpShaderParametersList[4] = &pMaterial->mHullShaderParameters, pMaterial->mpShaderParametersList[5] = &pMaterial->mDomainShaderParameters, pMaterial->mpShaderParametersList[6] = NULL; pMaterial->mpRenderStateBlock = mpRenderStateBlock; if( mpRenderStateBlock ) mpRenderStateBlock->AddRef(); pMaterial->mMaterialName = mMaterialName; pMaterial->mConfigBlock = mConfigBlock; // For each of the shader stages, bind shaders and buffers for( CPUTShaderParameters **pCur = pMaterial->mpShaderParametersList; *pCur; pCur++ ) // Bind textures and buffersfor each shader stage { pMaterial->BindTextures( **pCur, modelSuffix, meshSuffix ); pMaterial->BindBuffers( **pCur, modelSuffix, meshSuffix ); pMaterial->BindUAVs( **pCur, modelSuffix, meshSuffix ); pMaterial->BindConstantBuffers( **pCur, modelSuffix, meshSuffix ); } return pMaterial; } //----------------------------------------------------------------------------- bool CPUTMaterialDX11::MaterialRequiresPerModelPayload() { return (mpPixelShader && mpPixelShader ->ShaderRequiresPerModelPayload(mConfigBlock)) || (mpComputeShader && mpComputeShader ->ShaderRequiresPerModelPayload(mConfigBlock)) || (mpVertexShader && mpVertexShader ->ShaderRequiresPerModelPayload(mConfigBlock)) || (mpGeometryShader && mpGeometryShader->ShaderRequiresPerModelPayload(mConfigBlock)) || (mpHullShader && mpHullShader ->ShaderRequiresPerModelPayload(mConfigBlock)) || (mpDomainShader && mpDomainShader ->ShaderRequiresPerModelPayload(mConfigBlock)); } //----------------------------------------------------------------------------- void CPUTMaterialDX11::RebindTexturesAndBuffers() { for( CPUTShaderParameters **pCur = mpShaderParametersList; *pCur; pCur++ ) // Rebind textures for each shader stage { for( UINT ii=0; ii<(*pCur)->mTextureCount; ii++ ) { if( (*pCur)->mpTexture[ii] ) { UINT bindPoint = (*pCur)->mpTextureParameterBindPoint[ii]; SAFE_RELEASE((*pCur)->mppBindViews[bindPoint]); (*pCur)->mppBindViews[bindPoint] = ((CPUTTextureDX11*)(*pCur)->mpTexture[ii])->GetShaderResourceView(); (*pCur)->mppBindViews[bindPoint]->AddRef(); } } for( UINT ii=0; ii<(*pCur)->mBufferCount; ii++ ) { if( (*pCur)->mpBuffer[ii] ) { UINT bindPoint = (*pCur)->mpBufferParameterBindPoint[ii]; SAFE_RELEASE((*pCur)->mppBindViews[bindPoint]); (*pCur)->mppBindViews[bindPoint] = ((CPUTBufferDX11*)(*pCur)->mpBuffer[ii])->GetShaderResourceView(); (*pCur)->mppBindViews[bindPoint]->AddRef(); } } for( UINT ii=0; ii<(*pCur)->mUAVCount; ii++ ) { if( (*pCur)->mpUAV[ii] ) { UINT bindPoint = (*pCur)->mpUAVParameterBindPoint[ii]; SAFE_RELEASE((*pCur)->mppBindUAVs[bindPoint]); (*pCur)->mppBindUAVs[bindPoint] = ((CPUTBufferDX11*)(*pCur)->mpUAV[ii])->GetUnorderedAccessView(); (*pCur)->mppBindUAVs[bindPoint]->AddRef(); } } for( UINT ii=0; ii<(*pCur)->mConstantBufferCount; ii++ ) { if( (*pCur)->mpConstantBuffer[ii] ) { UINT bindPoint = (*pCur)->mpConstantBufferParameterBindPoint[ii]; SAFE_RELEASE((*pCur)->mppBindConstantBuffers[bindPoint]); (*pCur)->mppBindConstantBuffers[bindPoint] = ((CPUTBufferDX11*)(*pCur)->mpConstantBuffer[ii])->GetNativeBuffer(); (*pCur)->mppBindConstantBuffers[bindPoint]->AddRef(); } } } } //----------------------------------------------------------------------------- void CPUTMaterialDX11::ReleaseTexturesAndBuffers() { for( CPUTShaderParameters **pCur = mpShaderParametersList; *pCur; pCur++ ) // Release the buffers and views for each shader stage { if( *pCur ) { for( UINT ii=0; iimppBindViews[ii]); } for( UINT ii=0; iimppBindUAVs[ii]); } for( UINT ii=0; iimppBindConstantBuffers[ii]); } } } } //----------------------------------------------------------------------------- void CPUTShaderParameters::CloneShaderParameters( CPUTShaderParameters *pShaderParameter ) { pShaderParameter->mpTextureParameterName = new cString[mTextureParameterCount]; pShaderParameter->mpTextureParameterBindPoint = new UINT[ mTextureParameterCount]; pShaderParameter->mpSamplerParameterName = new cString[mSamplerParameterCount]; pShaderParameter->mpSamplerParameterBindPoint = new UINT[ mSamplerParameterCount]; pShaderParameter->mpBufferParameterName = new cString[mBufferParameterCount]; pShaderParameter->mpBufferParameterBindPoint = new UINT[ mBufferParameterCount]; pShaderParameter->mpUAVParameterName = new cString[mUAVParameterCount]; pShaderParameter->mpUAVParameterBindPoint = new UINT[ mUAVParameterCount]; pShaderParameter->mpConstantBufferParameterName = new cString[mConstantBufferParameterCount]; pShaderParameter->mpConstantBufferParameterBindPoint = new UINT[ mConstantBufferParameterCount]; pShaderParameter->mTextureCount = mTextureCount; pShaderParameter->mBufferCount = mBufferCount; pShaderParameter->mUAVCount = mUAVCount; pShaderParameter->mConstantBufferCount = mConstantBufferCount; pShaderParameter->mTextureParameterCount = mTextureParameterCount; pShaderParameter->mTextureParameterCount = mTextureParameterCount; pShaderParameter->mBufferParameterCount = mBufferParameterCount; pShaderParameter->mUAVParameterCount = mUAVParameterCount; pShaderParameter->mConstantBufferParameterCount = mConstantBufferParameterCount; for(UINT ii=0; iimpTextureParameterName[ii] = mpTextureParameterName[ii]; pShaderParameter->mpTextureParameterBindPoint[ii] = mpTextureParameterBindPoint[ii]; } for(UINT ii=0; iimpSamplerParameterName[ii] = mpSamplerParameterName[ii]; pShaderParameter->mpSamplerParameterBindPoint[ii] = mpSamplerParameterBindPoint[ii]; } for(UINT ii=0; iimpBufferParameterName[ii] = mpBufferParameterName[ii]; pShaderParameter->mpBufferParameterBindPoint[ii] = mpBufferParameterBindPoint[ii]; } for(UINT ii=0; iimpUAVParameterName[ii] = mpUAVParameterName[ii]; pShaderParameter->mpUAVParameterBindPoint[ii] = mpUAVParameterBindPoint[ii]; } for(UINT ii=0; iimpConstantBufferParameterName[ii] = mpConstantBufferParameterName[ii]; pShaderParameter->mpConstantBufferParameterBindPoint[ii] = mpConstantBufferParameterBindPoint[ii]; } pShaderParameter->mBindViewMin = mBindViewMin; pShaderParameter->mBindViewMax = mBindViewMax; pShaderParameter->mBindUAVMin = mBindUAVMin; pShaderParameter->mBindUAVMax = mBindUAVMax; pShaderParameter->mBindConstantBufferMin = mBindConstantBufferMin; pShaderParameter->mBindConstantBufferMax = mBindConstantBufferMax; } ================================================ FILE: CPUT/CPUT/CPUTMaterialDX11.h ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #ifndef __CPUTMATERIALDX11_H__ #define __CPUTMATERIALDX11_H__ #include #include "CPUTMaterial.h" class CPUTPixelShaderDX11; class CPUTComputeShaderDX11; class CPUTVertexShaderDX11; class CPUTGeometryShaderDX11; class CPUTHullShaderDX11; class CPUTDomainShaderDX11; class CPUTShaderParameters { public: UINT mTextureCount; cString *mpTextureParameterName; UINT *mpTextureParameterBindPoint; UINT mTextureParameterCount; CPUTTexture *mpTexture[CPUT_MATERIAL_MAX_TEXTURE_SLOTS]; CPUTBuffer *mpBuffer[CPUT_MATERIAL_MAX_BUFFER_SLOTS]; CPUTBuffer *mpUAV[CPUT_MATERIAL_MAX_UAV_SLOTS]; CPUTBuffer *mpConstantBuffer[CPUT_MATERIAL_MAX_CONSTANT_BUFFER_SLOTS]; cString *mpSamplerParameterName; UINT *mpSamplerParameterBindPoint; UINT mSamplerParameterCount; UINT mBufferCount; UINT mBufferParameterCount; cString *mpBufferParameterName; UINT *mpBufferParameterBindPoint; UINT mUAVCount; UINT mUAVParameterCount; cString *mpUAVParameterName; UINT *mpUAVParameterBindPoint; UINT mConstantBufferCount; UINT mConstantBufferParameterCount; cString *mpConstantBufferParameterName; UINT *mpConstantBufferParameterBindPoint; UINT mBindViewMin; UINT mBindViewMax; UINT mBindUAVMin; UINT mBindUAVMax; UINT mBindConstantBufferMin; UINT mBindConstantBufferMax; ID3D11ShaderResourceView *mppBindViews[CPUT_MATERIAL_MAX_SRV_SLOTS]; ID3D11UnorderedAccessView *mppBindUAVs[CPUT_MATERIAL_MAX_UAV_SLOTS]; ID3D11Buffer *mppBindConstantBuffers[CPUT_MATERIAL_MAX_CONSTANT_BUFFER_SLOTS]; CPUTShaderParameters() : mTextureCount(0), mTextureParameterCount(0), mpTextureParameterName(NULL), mpTextureParameterBindPoint(NULL), mSamplerParameterCount(0), mpSamplerParameterName(NULL), mpSamplerParameterBindPoint(NULL), mBufferCount(0), mBufferParameterCount(0), mpBufferParameterName(NULL), mpBufferParameterBindPoint(NULL), mUAVCount(0), mUAVParameterCount(0), mpUAVParameterName(NULL), mpUAVParameterBindPoint(NULL), mConstantBufferCount(0), mConstantBufferParameterCount(0), mpConstantBufferParameterName(NULL), mpConstantBufferParameterBindPoint(NULL), mBindViewMin(CPUT_MATERIAL_MAX_SRV_SLOTS), mBindViewMax(0), mBindUAVMin(CPUT_MATERIAL_MAX_UAV_SLOTS), mBindUAVMax(0), mBindConstantBufferMin(CPUT_MATERIAL_MAX_CONSTANT_BUFFER_SLOTS), mBindConstantBufferMax(0) { // initialize texture slot list to null for(int ii=0; ii /* * Constants */ static const float kEpsilon = 0.00001f; static const float kPi = 3.14159265358979323846f; static const float k2Pi = kPi*2.0f; static const float kPiDiv2 = kPi/2.0f; static const float kInvPi = 1.0f/kPi; static const float kDegToRad = (kPi/180.0f); static const float kRadToDeg = (180.0f/kPi); static inline float DegToRad( float fDeg ) { return fDeg * kDegToRad; } static inline float RadToDeg( float fRad ) { return fRad * kRadToDeg; } template static inline void Swap(T &a, T &b) { T t = b; b = a; a = t; } struct float2; struct float3; struct float4; /**************************************\ float2 \**************************************/ struct float2 { union { struct { float x; float y; }; float f[2]; }; /***************************************\ | Constructors | \***************************************/ float2() {} explicit float2(float f) : x(f), y(f) { } explicit float2(float _x, float _y) : x(_x), y(_y) { } explicit float2(float* f) : x(f[0]), y(f[1]) { } float2(const float2 &v) : x(v.x), y(v.y) { } const float2 &operator=(const float2 &v) { x = v.x; y = v.y; return *this; } /***************************************\ | Basic math operations | \***************************************/ float2 operator+(const float2 &r) const { return float2(x+r.x, y+r.y); } const float2 &operator+=(const float2 &r) { x += r.x; y += r.y; return *this; } float2 operator-(const float2 &r) const { return float2(x-r.x, y-r.y); } const float2 &operator-=(const float2 &r) { x -= r.x; y -= r.y; return *this; } /***************************************\ | Basic math operations with scalars | \***************************************/ float2 operator+(float f) const { return float2(x+f, y+f); } const float2 &operator+=(float f) { x += f; y += f; return *this; } float2 operator-(float f) const { return float2(x-f, y-f); } const float2 &operator-=(float f) { x -= f; y -= f; return *this; } float2 operator*(float f) const { return float2(x*f, y*f); } const float2 &operator*=(float f) { x *= f; y *= f; return *this; } float2 operator/(float f) const { return float2(x/f, y/f); } const float2 &operator/=(float f) { x /= f; y /= f; return *this; } /***************************************\ | Other math | \***************************************/ // Equality bool operator==(const float2 &r) const { return (x==r.x && y == r.y); } bool operator!=(const float2 &r) const { return !(*this == r); } // Hadd float hadd(void) const { return x + y; } // Length float lengthSq(void) const { return x*x + y*y; } float length(void) const { return sqrtf(lengthSq()); } void normalize(void) { *this /= length(); } }; inline float dot2(const float2 &l, const float2 &r) { return l.x*r.x + l.y*r.y; } inline float2 normalize(const float2 &v) { float length = v.length(); return v / length; } /**************************************\ float3 \**************************************/ struct float3 { union { struct { float x; float y; float z; }; float f[3]; }; /***************************************\ | Constructors | \***************************************/ float3() {} explicit float3(float f) : x(f), y(f), z(f) { } explicit float3(float _x, float _y, float _z) : x(_x), y(_y), z(_z) { } explicit float3(float* f) : x(f[0]), y(f[1]), z(f[2]) { } float3(const float3 &v) : x(v.x), y(v.y), z(v.z) { } float3(const float4 &v); const float3 &operator=(const float3 &v) { x = v.x; y = v.y; z = v.z; return *this; } /***************************************\ | Basic math operations | \***************************************/ float3 operator+(const float3 &r) const { return float3(x+r.x, y+r.y, z+r.z); } const float3 &operator+=(const float3 &r) { x += r.x; y += r.y; z += r.z; return *this; } float3 operator-(const float3 &r) const { return float3(x-r.x, y-r.y, z-r.z); } const float3 &operator-=(const float3 &r) { x -= r.x; y -= r.y; z -= r.z; return *this; } float3 operator*(const float3 &r) const { return float3(x*r.x, y*r.y, z*r.z); } const float3 &operator*=(const float3 &r) { x *= r.x; y *= r.y; z *= r.z; return *this; } float3 operator/(const float3 &r) const { return float3(x/r.x, y/r.y, z/r.z); } const float3 &operator/=(const float3 &r) { x /= r.x; y /= r.y; z /= r.z; return *this; } /***************************************\ | Basic math operations with scalars | \***************************************/ float3 operator+(float f) const { return float3(x+f, y+f, z+f); } const float3 &operator+=(float f) { x += f; y += f; z += f; return *this; } float3 operator-(float f) const { return float3(x-f, y-f, z-f); } const float3 &operator-=(float f) { x -= f; y -= f; z -= f; return *this; } float3 operator*(float f) const { return float3(x*f, y*f, z*f); } const float3 &operator*=(float f) { x *= f; y *= f; z *= f; return *this; } float3 operator/(float f) const { return float3(x/f, y/f, z/f); } const float3 &operator/=(float f) { x /= f; y /= f; z /= f; return *this; } /***************************************\ | Other math | \***************************************/ // Equality bool operator==(const float3 &r) const { return x==r.x &&y == r.y &&z == r.z; } bool operator!=(const float3 &r) const { return !(*this == r); } // Hadd float hadd(void) const { return x + y + z; } // Length float lengthSq(void) const { return x*x + y*y + z*z; } float length(void) const { return sqrtf(lengthSq()); } float3 normalize(void) { return (*this /= length()); } }; inline float dot3(const float3 &l, const float3 &r) { return l.x*r.x + l.y*r.y + l.z*r.z; } inline float3 cross3(const float3 &l, const float3 &r) { return float3( l.y*r.z-l.z*r.y, l.z*r.x-l.x*r.z, l.x*r.y-l.y*r.x); } inline float3 normalize(const float3 &v) { float length = v.length(); return v / length; } /**************************************\ float4 \**************************************/ __declspec(align(16)) struct float4 { union { struct { float x; float y; float z; float w; }; float f[4]; }; /***************************************\ | Constructors | \***************************************/ float4() {} explicit float4(float f) : x(f), y(f), z(f), w(f) { } explicit float4(float _x, float _y, float _z, float _w) : x(_x), y(_y), z(_z), w(_w) { } explicit float4(float* f) : x(f[0]), y(f[1]), z(f[2]), w(f[3]) { } float4(const float4 &v) : x(v.x), y(v.y), z(v.z), w(v.w) { } float4(const float3 &v, float _w) : x(v.x), y(v.y), z(v.z), w(_w) { } const float4 &operator=(const float4 &v) { x = v.x; y = v.y; z = v.z; w = v.w; return *this; } /***************************************\ | Basic math operations | \***************************************/ float4 operator+(const float4 &r) const { return float4(x+r.x, y+r.y, z+r.z, w+r.w); } const float4 &operator+=(const float4 &r) { x += r.x; y += r.y; z += r.z; w += r.w; return *this; } float4 operator-(const float4 &r) const { return float4(x-r.x, y-r.y, z-r.z, w-r.w); } const float4 &operator-=(const float4 &r) { x -= r.x; y -= r.y; z -= r.z; w -= r.w; return *this; } float4 operator*(const float4 &r) const { return float4(x*r.x, y*r.y, z*r.z, w*r.w); } const float4 &operator*=(const float4 &r) { x *= r.x; y *= r.y; z *= r.z; w *= r.w; return *this; } float4 operator/(const float4 &r) const { return float4(x/r.x, y/r.y, z/r.z, w/r.w); } const float4 &operator/=(const float4 &r) { x /= r.x; y /= r.y; z /= r.z; w /= r.w; return *this; } /***************************************\ | Basic math operations with scalars | \***************************************/ float4 operator+(float f) const { return float4(x+f, y+f, z+f, w+f); } const float4 &operator+=(float f) { x += f; y += f; z += f; return *this; } float4 operator-(float f) const { return float4(x-f, y-f, z-f, w-f); } const float4 &operator-=(float f) { x -= f; y -= f; z -= f; w -= f; return *this; } float4 operator*(float f) const { return float4(x*f, y*f, z*f, w*f); } const float4 &operator*=(float f) { x *= f; y *= f; z *= f; w *= f; return *this; } float4 operator/(float f) const { return float4(x/f, y/f, z/f, w/f); } const float4 &operator/=(float f) { x /= f; y /= f; z /= f; w /= f; return *this; } /***************************************\ | Other math | \***************************************/ // Equality bool operator==(const float4 &r) const { return x==r.x && y == r.y && z == r.z && w == r.w; } bool operator!=(const float4 &r) const { return !(*this == r); } // Hadd float hadd(void) const { return x + y + z + w; } // Length float lengthSq(void) const { return x*x + y*y + z*z + w*w; } float length(void) const { return sqrtf(lengthSq()); } void normalize(void) { *this /= length(); } }; inline float dot4(const float4 &l, const float4 &r) { return l.x*r.x + l.y*r.y + l.z*r.z + l.w*r.w; } inline float4 normalize(const float4 &v) { float length = v.length(); return v / length; } inline float3::float3(const float4 &v) : x(v.x), y(v.y), z(v.z) { } /**************************************\ float3x3 \**************************************/ struct float4x4; struct float3x3 { struct { float3 r0; float3 r1; float3 r2; }; /***************************************\ | Constructors | \***************************************/ float3x3() {} explicit float3x3(float f) : r0(f), r1(f), r2(f) { } explicit float3x3(float* f) : r0(f+0), r1(f+3), r2(f+6) { } float3x3(const float3 &_r0, const float3 &_r1, const float3 &_r2) : r0(_r0), r1(_r1), r2(_r2) { } float3x3(float _m00, float _m01, float _m02, float _m10, float _m11, float _m12, float _m20, float _m21, float _m22) : r0(_m00,_m01,_m02) , r1(_m10,_m11,_m12) , r2(_m20,_m21,_m22) { } float3x3(const float4x4 &m); const float3x3 &operator=(const float3x3 &m) { r0 = m.r0; r1 = m.r1; r2 = m.r2; return *this; } /***************************************\ | Basic math operations | \***************************************/ #define MTX3_INDEX(f,r,c) ((f)[(r*3)+c]) inline float3x3 operator*(const float3x3 &r) const { float3x3 m(1,0,0,0,1,0,0,0,1); const float* left = (const float*)&this->r0; const float* right = (const float*)&r.r0; float* result = (float*)&m; int ii, jj, kk; for(ii=0; ii<3; ++ii) /* row */ { for(jj=0; jj<3; ++jj) /* column */ { float sum = MTX3_INDEX(left,ii,0)*MTX3_INDEX(right,0,jj); for(kk=1; kk<3; ++kk) { sum += (MTX3_INDEX(left,ii,kk)*MTX3_INDEX(right,kk,jj)); } MTX3_INDEX(result,ii,jj) = sum; } } return m; } #undef MTX3_INDEX inline float3 operator*(const float3 &v) const { float3 a; a.x = (r0*v).hadd(); a.y = (r1*v).hadd(); a.z = (r2*v).hadd(); return a; } /***************************************\ | Basic math operations with scalars | \***************************************/ float3x3 operator+(float f) const { return float3x3(r0+f, r1+f, r2+f); } const float3x3 &operator+=(float f) { r0 += f; r1 += f; r2 += f; return *this; } float3x3 operator-(float f) const { return float3x3(r0-f, r1-f, r2-f); } const float3x3 &operator-=(float f) { r0 -= f; r1 -= f; r2 -= f; return *this; } float3x3 operator*(float f) const { return float3x3(r0*f, r1*f, r2*f); } const float3x3 &operator*=(float f) { r0 *= f; r1 *= f; r2 *= f; return *this; } float3x3 operator/(float f) const { return float3x3(r0/f, r1/f, r2/f); } const float3x3 &operator/=(float f) { r0 /= f; r1 /= f; r2 /= f; return *this; } /***************************************\ | Other math | \***************************************/ // Equality bool operator==(const float3x3 &r) const { return r0 == r.r0 && r1 == r.r1 && r2 == r.r2; } bool operator!=(const float3x3 &r) const { return !(*this == r); } float determinant() const { float f0 = r0.x * (r1.y*r2.z-r2.y*r1.z); float f1 = r0.y * -(r1.x*r2.z-r2.x*r1.z); float f2 = r0.z * (r1.x*r2.y-r2.x*r1.y); return f0 + f1 + f2; } void transpose(void) { Swap(r0.y, r1.x); Swap(r0.z, r2.x); Swap(r1.z, r2.y); } void invert(void) { float det = determinant(); float3x3 inv; inv.r0.x = (r1.y*r2.z) - (r1.z*r2.y); inv.r0.y = -((r1.x*r2.z) - (r1.z*r2.x)); inv.r0.z = (r1.x*r2.y) - (r1.y*r2.x); inv.r1.x = -((r0.y*r2.z) - (r0.z*r2.y)); inv.r1.y = (r0.x*r2.z) - (r0.z*r2.x); inv.r1.z = -((r0.x*r2.y) - (r0.y*r2.x)); inv.r2.x = (r0.y*r1.z) - (r0.z*r1.y); inv.r2.y = -((r0.x*r1.z) - (r0.z*r1.x)); inv.r2.z = (r0.x*r1.y) - (r0.y*r1.x); inv.transpose(); inv /= det; *this = inv; } }; inline float3x3 float3x3Identity(void) { return float3x3(1,0,0, 0,1,0, 0,0,1); } inline float determinant(const float3x3&m) { return m.determinant(); } inline float3x3 transpose(const float3x3 &m) { float3x3 ret = m; ret.transpose(); return ret; } inline float3x3 inverse(const float3x3 &m) { float3x3 ret = m; ret.invert(); return ret; } inline float3x3 float3x3RotationX(float rad) { float c = cosf( rad ); float s = sinf( rad ); float3x3 m( 1.0f, 0.0f, 0.0f, 0.0f, c, s, 0.0f, -s, c ); return m; } inline float3x3 float3x3RotationY(float rad) { float c = cosf( rad ); float s = sinf( rad ); float3x3 m( c, 0.0f, -s, 0.0f, 1.0f, 0.0f, s, 0.0f, c ); return m; } inline float3x3 float3x3RotationZ(float rad) { float c = cosf( rad ); float s = sinf( rad ); float3x3 m( c, s, 0.0f, -s, c, 0.0f, 0.0f, 0.0f, 1.0f ); return m; } inline float3x3 float3x3RotationAxis(const float3 &axis, float rad ) { float3 normAxis = normalize(axis); float c = cosf(rad); float s = sinf(rad); float t = 1 - c; float x = normAxis.x; float y = normAxis.y; float z = normAxis.z; float3x3 m; m.r0.x = (t * x * x) + c; m.r0.y = (t * x * y) + s * z; m.r0.z = (t * x * z) - s * y; m.r1.x = (t * x * y) - (s * z); m.r1.y = (t * y * y) + c; m.r1.z = (t * y * z) + (s * x); m.r2.x = (t * x * z) + (s * y); m.r2.y = (t * y * z) - (s * x); m.r2.z = (t * z * z) + c; return m; } inline float3x3 float3x3Scale(float x, float y, float z) { return float3x3(x,0,0, 0,y,0, 0,0,z); } /**************************************\ float4x4 \**************************************/ struct float4x4 { struct { float4 r0; float4 r1; float4 r2; float4 r3; }; /***************************************\ | Constructors | \***************************************/ float4x4() {} explicit float4x4(float f) : r0(f), r1(f), r2(f), r3(f) { } explicit float4x4(float* f) : r0(f+0), r1(f+4), r2(f+8), r3(f+12) { } float4x4(const float4 &_r0, const float4 &_r1, const float4 &_r2, const float4 &_r3) : r0(_r0), r1(_r1), r2(_r2), r3(_r3) { } float4x4(float _m00, float _m01, float _m02, float _m03, float _m10, float _m11, float _m12, float _m13, float _m20, float _m21, float _m22, float _m23, float _m30, float _m31, float _m32, float _m33) : r0(_m00,_m01,_m02,_m03) , r1(_m10,_m11,_m12,_m13) , r2(_m20,_m21,_m22,_m23) , r3(_m30,_m31,_m32,_m33) { } float4x4(const float3x3 &m) : r0(m.r0, 0.0f) , r1(m.r1, 0.0f) , r2(m.r2, 0.0f) , r3(0.0f, 0.0f, 0.0f, 1.0f) { } const float4x4 &operator=(const float4x4 &m) { r0 = m.r0; r1 = m.r1; r2 = m.r2; r3 = m.r3; return *this; } /***************************************\ | Basic math operations | \***************************************/ #define MTX4_INDEX(f,r,c) ((f)[(r*4)+c]) inline float4x4 operator*(const float4x4 &r) const { float4x4 m(1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1); const float* left = (const float*)&this->r0; const float* right = (const float*)&r.r0; float* result = (float*)&m; int ii, jj, kk; for(ii=0; ii<4; ++ii) /* row */ { for(jj=0; jj<4; ++jj) /* column */ { float sum = MTX4_INDEX(left,ii,0)*MTX4_INDEX(right,0,jj); for(kk=1; kk<4; ++kk) { sum += (MTX4_INDEX(left,ii,kk)*MTX4_INDEX(right,kk,jj)); } MTX4_INDEX(result,ii,jj) = sum; } } return m; } #undef MTX4_INDEX inline float4 operator*(const float4 &v) const { float4 a; a.x = (r0*v).hadd(); a.y = (r1*v).hadd(); a.z = (r2*v).hadd(); a.w = (r3*v).hadd(); return a; } /***************************************\ | Basic math operations with scalars | \***************************************/ float4x4 operator+(float f) const { return float4x4(r0+f, r1+f, r2+f, r3+f); } const float4x4 &operator+=(float f) { r0 += f; r1 += f; r2 += f; r3 += f; return *this; } float4x4 operator-(float f) const { return float4x4(r0-f, r1-f, r2-f, r3-f); } const float4x4 &operator-=(float f) { r0 -= f; r1 -= f; r2 -= f; r3 -= f; return *this; } float4x4 operator*(float f) const { return float4x4(r0*f, r1*f, r2*f, r3*f); } const float4x4 &operator*=(float f) { r0 *= f; r1 *= f; r2 *= f; r3 *= f; return *this; } float4x4 operator/(float f) const { return float4x4(r0/f, r1/f, r2/f, r3/f); } const float4x4 &operator/=(float f) { r0 /= f; r1 /= f; r2 /= f; r3 /= f; return *this; } /***************************************\ | Other math | \***************************************/ // Equality bool operator==(const float4x4 &r) const { return r0 == r.r0 && r1 == r.r1 && r2 == r.r2 && r3 == r.r3; } bool operator!=(const float4x4 &r) const { return !(*this == r); } float determinant() const { float det = 0.0f; float3x3 a( r1.y,r1.z,r1.w, r2.y,r2.z,r2.w, r3.y,r3.z,r3.w); float3x3 b( r1.x,r1.z,r1.w, r2.x,r2.z,r2.w, r3.x,r3.z,r3.w); float3x3 c( r1.x,r1.y,r1.w, r2.x,r2.y,r2.w, r3.x,r3.y,r3.w); float3x3 d( r1.x,r1.y,r1.z, r2.x,r2.y,r2.z, r3.x,r3.y,r3.z); det += r0.x * a.determinant(); det -= r0.y * b.determinant(); det += r0.z * c.determinant(); det -= r0.w * d.determinant(); return det; } void transpose(void) { Swap(r0.y, r1.x); Swap(r0.z, r2.x); Swap(r0.w, r3.x); Swap(r1.z, r2.y); Swap(r1.w, r3.y); Swap(r2.w, r3.z); } void invert(void) { float4x4 ret; float recip; /* temp matrices */ /* row 1 */ float3x3 a( r1.y,r1.z,r1.w, r2.y,r2.z,r2.w, r3.y,r3.z,r3.w); float3x3 b( r1.x,r1.z,r1.w, r2.x,r2.z,r2.w, r3.x,r3.z,r3.w); float3x3 c( r1.x,r1.y,r1.w, r2.x,r2.y,r2.w, r3.x,r3.y,r3.w); float3x3 d( r1.x,r1.y,r1.z, r2.x,r2.y,r2.z, r3.x,r3.y,r3.z); /* row 2 */ float3x3 e( r0.y,r0.z,r0.w, r2.y,r2.z,r2.w, r3.y,r3.z,r3.w); float3x3 f( r0.x,r0.z,r0.w, r2.x,r2.z,r2.w, r3.x,r3.z,r3.w); float3x3 g( r0.x,r0.y,r0.w, r2.x,r2.y,r2.w, r3.x,r3.y,r3.w); float3x3 h( r0.x,r0.y,r0.z, r2.x,r2.y,r2.z, r3.x,r3.y,r3.z); /* row 3 */ float3x3 i( r0.y,r0.z,r0.w, r1.y,r1.z,r1.w, r3.y,r3.z,r3.w); float3x3 j( r0.x,r0.z,r0.w, r1.x,r1.z,r1.w, r3.x,r3.z,r3.w); float3x3 k( r0.x,r0.y,r0.w, r1.x,r1.y,r1.w, r3.x,r3.y,r3.w); float3x3 l( r0.x,r0.y,r0.z, r1.x,r1.y,r1.z, r3.x,r3.y,r3.z); /* row 4 */ float3x3 m( r0.y, r0.z, r0.w, r1.y, r1.z, r1.w, r2.y, r2.z, r2.w); float3x3 n( r0.x, r0.z, r0.w, r1.x, r1.z, r1.w, r2.x, r2.z, r2.w); float3x3 o( r0.x,r0.y,r0.w, r1.x,r1.y,r1.w, r2.x,r2.y,r2.w); float3x3 p( r0.x,r0.y,r0.z, r1.x,r1.y,r1.z, r2.x,r2.y,r2.z); /* row 1 */ ret.r0.x = a.determinant(); ret.r0.y = -b.determinant(); ret.r0.z = c.determinant(); ret.r0.w = -d.determinant(); /* row 2 */ ret.r1.x = -e.determinant(); ret.r1.y = f.determinant(); ret.r1.z = -g.determinant(); ret.r1.w = h.determinant(); /* row 3 */ ret.r2.x = i.determinant(); ret.r2.y = -j.determinant(); ret.r2.z = k.determinant(); ret.r2.w = -l.determinant(); /* row 4 */ ret.r3.x = -m.determinant(); ret.r3.y = n.determinant(); ret.r3.z = -o.determinant(); ret.r3.w = p.determinant(); ret.transpose(); recip = 1.0f/determinant(); ret *= recip; *this = ret; } // Axis access float3 getXAxis(void) const { return r0; } float3 getYAxis(void) const { return r1; } float3 getZAxis(void) const { return r2; } float3 getPosition(void) const { return float3(r3); } void orthonormalize(void) { float3 x = getXAxis(); float3 y = getYAxis(); float3 z; x.normalize(); z = normalize(cross3(x, y)); y = normalize(cross3(z, x)); r0 = float4(x, 0.0f); r1 = float4(y, 0.0f); r2 = float4(z, 0.0f); } }; inline float3x3::float3x3(const float4x4 &m) : r0(m.r0) , r1(m.r1) , r2(m.r2) { } inline float determinant(const float4x4&m) { return m.determinant(); } inline float4x4 float4x4Identity(void) { return float4x4(1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1); } inline float4x4 transpose(const float4x4 &m) { float4x4 ret = m; ret.transpose(); return ret; } inline float4x4 inverse(const float4x4 &m) { float4x4 ret = m; ret.invert(); return ret; } inline float4x4 float4x4RotationX(float rad) { float c = cosf( rad ); float s = sinf( rad ); float4x4 m( 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, c, s, 0.0f, 0.0f, -s, c, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f); return m; } inline float4x4 float4x4RotationY(float rad) { float c = cosf( rad ); float s = sinf( rad ); float4x4 m( c, 0.0f, -s, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, s, 0.0f, c, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f); return m; } inline float4x4 float4x4RotationZ(float rad) { float c = cosf( rad ); float s = sinf( rad ); float4x4 m( c, s, 0.0f, 0.0f, -s, c, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f); return m; } inline float4x4 float4x4RotationAxis(const float3 &axis, float rad ) { float3 normAxis = normalize(axis); float c = cosf(rad); float s = sinf(rad); float t = 1 - c; float x = normAxis.x; float y = normAxis.y; float z = normAxis.z; float4x4 m = float4x4Identity(); m.r0.x = (t * x * x) + c; m.r0.y = (t * x * y) + s * z; m.r0.z = (t * x * z) - s * y; m.r1.x = (t * x * y) - (s * z); m.r1.y = (t * y * y) + c; m.r1.z = (t * y * z) + (s * x); m.r2.x = (t * x * z) + (s * y); m.r2.y = (t * y * z) - (s * x); m.r2.z = (t * z * z) + c; return m; } inline float4x4 float4x4Scale(float x, float y, float z) { return float4x4(x,0,0,0, 0,y,0,0, 0,0,z,0, 0,0,0,1); } inline float4x4 float4x4Translation(float x, float y, float z) { float4x4 m = float4x4Identity(); m.r3.x = x; m.r3.y = y; m.r3.z = z; return m; } inline float4x4 float4x4Translation(const float3 &v) { return float4x4Translation(v.x, v.y, v.z); } inline float4x4 float4x4PerspectiveFovLH(float fov, float aspect, float nearPlane, float farPlane) { float height = tanf(fov * 0.5f); float width = height * aspect; float diff = farPlane-nearPlane; float div = farPlane / diff; float4x4 m( 1.0f/width, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f/height, 0.0f, 0.0f, 0.0f, 0.0f, div, 1.0f, 0.0f, 0.0f, -nearPlane*div, 0.0f ); return m; } inline float4x4 float4x4PerspectiveFovRH(float fov, float aspect, float nearPlane, float farPlane) { float4x4 m = float4x4Identity(); float f = tanf(kPiDiv2 - (fov/2.0f)); float diff = nearPlane-farPlane; float div = farPlane / diff; m.r0.x = f/aspect; m.r1.y = f; m.r2.z = div; m.r2.w = -1; m.r3.z = nearPlane * div; m.r3.w = 0; return m; } inline float4x4 float4x4PerspectiveLH(float width, float height, float nearPlane, float farPlane) { float4x4 m = float4x4Identity(); m.r0.x = 2*nearPlane/width; m.r1.y = 2*nearPlane/height; m.r2.z = farPlane/(farPlane-nearPlane); m.r2.w = 1; m.r3.z = nearPlane*farPlane/(nearPlane-farPlane); m.r3.w = 0; return m; } inline float4x4 float4x4PerspectiveRH(float width, float height, float nearPlane, float farPlane) { float4x4 m = float4x4Identity(); m.r0.x = 2*nearPlane/width; m.r1.y = 2*nearPlane/height; m.r2.z = farPlane/(nearPlane-farPlane); m.r2.w = -1; m.r3.z = nearPlane*farPlane/(nearPlane-farPlane); m.r3.w = 0; return m; } inline float4x4 float4x4OrthographicOffCenterLH(float left, float right, float top, float bottom, float nearPlane, float farPlane) { float4x4 m = float4x4Identity(); float diff = farPlane-nearPlane; m.r0.x = 2.0f/(right-left); m.r1.y = 2.0f/(top-bottom); m.r2.z = 1.0f/diff; m.r3.x = -((left+right)/(right-left)); m.r3.y = -((top+bottom)/(top-bottom)); m.r3.z = -nearPlane/diff; return m; } inline float4x4 float4x4OrthographicOffCenterRH(float left, float right, float top, float bottom, float nearPlane, float farPlane) { float4x4 m = float4x4Identity(); float diff = nearPlane-farPlane; m.r0.x = 2.0f/(right-left); m.r1.y = 2.0f/(top-bottom); m.r2.z = 1.0f/diff; m.r3.x = -((left+right)/(right-left)); m.r3.y = -((top+bottom)/(top-bottom)); m.r3.z = nearPlane/diff; return m; } inline float4x4 float4x4OrthographicLH(float width, float height, float nearPlane, float farPlane) { float halfWidth = width/2.0f; float halfHeight = height/2.0f; return float4x4OrthographicOffCenterLH(-halfWidth, halfWidth, halfHeight, -halfHeight, nearPlane, farPlane); } inline float4x4 float4x4OrthographicRH(float width, float height, float nearPlane, float farPlane) { float halfWidth = width/2.0f; float halfHeight = height/2.0f; return float4x4OrthographicOffCenterRH(-halfWidth, halfWidth, halfHeight, -halfHeight, nearPlane, farPlane); } /**************************************\ Quaternion \**************************************/ struct quaternion : public float4 { /***************************************\ | Constructors | \***************************************/ quaternion() {} explicit quaternion(float f) : float4(f) { } explicit quaternion(float _x, float _y, float _z, float _w) : float4(_x,_y,_z,_w) { } explicit quaternion(float* f) : float4(f) { } quaternion(const quaternion &v) : float4(v) { } quaternion(const float3 &v, float _w) { float3 norm = ::normalize(v); float a = _w*0.5f; float s = sinf(a); x = norm.x*s; y = norm.y*s; z = norm.z*s; w = cosf(a); } const quaternion &operator=(const quaternion &v) { x = v.x; y = v.y; z = v.z; w = v.w; return *this; } /* Methods */ float3 getXAxis(void) const { float3 ret( 1-2*(y*y+z*z), 2*(x*y+w*z), 2*(x*z-y*w)); return ret; } float3 getYAxis(void) const { float3 ret( 2*(x*y-z*w), 1-2*(x*x+z*z), 2*(y*z+x*w)); return ret; } float3 getZAxis(void) const { float3 ret( 2*(x*z+y*w), 2*(y*z-x*w), 1-2*(x*x+y*y)); return ret; } float3x3 getMatrix(void) const { quaternion q = *this; q.normalize(); float xx = q.x * q.x; float yy = q.y * q.y; float zz = q.z * q.z; float xy = q.x * q.y; float zw = q.z * q.w; float xz = q.x * q.z; float yw = q.y * q.w; float yz = q.y * q.z; float xw = q.x * q.w; float3x3 ret(1 - 2*(yy+zz), 2*(xy+zw), 2*(xz-yw), 2*(xy-zw), 1 - 2*(xx+zz), 2*(yz+xw), 2*(xz+yw), 2*(yz-xw), 1 - 2*(xx+yy) ); return ret; } quaternion conjugate(void) const { return quaternion(-x, -y, -z, w); } quaternion inverse(void) const { // Note: Only normalized quaternions are supportted at the moment quaternion q = *this; q.normalize(); return q.conjugate(); } }; inline quaternion quaternionMultiply(const quaternion &l, const quaternion &r) { quaternion q( r.w*l.x + r.x*l.w + r.y*l.z - r.z*l.y, r.w*l.y + r.y*l.w + r.z*l.x - r.x*l.z, r.w*l.z + r.z*l.w + r.x*l.y - r.y*l.x, r.w*l.w - r.x*l.x - r.y*l.y - r.z*l.z ); return q; } inline quaternion quaternionIdentity(void) { return quaternion(0.0f, 0.0f, 0.0f, 1.0f); } inline float3 Min( float3 &v0, float3 &v1 ) { float3 result; result.x = v0.x < v1.x ? v0.x : v1.x; result.y = v0.y < v1.y ? v0.y : v1.y; result.z = v0.z < v1.z ? v0.z : v1.z; return result; } inline float3 Max( float3 &v0, float3 &v1 ) { float3 result; result.x = v0.x > v1.x ? v0.x : v1.x; result.y = v0.y > v1.y ? v0.y : v1.y; result.z = v0.z > v1.z ? v0.z : v1.z; return result; } inline float4 Min( float4 &v0, float4 &v1 ) { float4 result; result.x = v0.x < v1.x ? v0.x : v1.x; result.y = v0.y < v1.y ? v0.y : v1.y; result.z = v0.z < v1.z ? v0.z : v1.z; result.w = v0.w < v1.w ? v0.w : v1.w; return result; } inline float4 Max( float4 &v0, float4 &v1 ) { float4 result; result.x = v0.x > v1.x ? v0.x : v1.x; result.y = v0.y > v1.y ? v0.y : v1.y; result.z = v0.z > v1.z ? v0.z : v1.z; result.w = v0.w > v1.w ? v0.w : v1.w; return result; } inline float4 operator*(const float4 &v, const float4x4 &m) { float4 result; result = m.r0 * v.x; result += m.r1 * v.y; result += m.r2 * v.z; result += m.r3 * v.w; return result; } #endif // #ifndef __CPUTMath_h__ ================================================ FILE: CPUT/CPUT/CPUTMesh.cpp ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #include "CPUTMesh.h" //----------------------------------------------------------------------------- // Note: The indices of these strings must match the corresponding value in enum CPUT_VERTEX_ELEMENT_SEMANTIC const char *CPUT_VERTEX_ELEMENT_SEMANTIC_AS_STRING[] = { "UNDEFINED", "UNDEFINED", // Note 1 is missing (back compatibility) "POSITON", "NORMAL", "TEXTURECOORD", "COLOR", "TANGENT", "BINORMAL" }; //----------------------------------------------------------------------------- void CPUTVertexElementDesc::Read(std::ifstream &meshFile) { meshFile.read((char*)this, sizeof(*this)); } //----------------------------------------------------------------------------- void CPUTRawMeshData::Allocate(__int32 numElements) { mVertexCount = numElements; mStride += mPaddingSize; // TODO: move this to stride computation mTotalVerticesSizeInBytes = mVertexCount * mStride; mpVertices = (void*)new char[(UINT)mTotalVerticesSizeInBytes]; ::memset( mpVertices, 0, (size_t)mTotalVerticesSizeInBytes ); } //----------------------------------------------------------------------------- bool CPUTRawMeshData::Read(std::ifstream &modelFile) { unsigned __int32 magicCookie; modelFile.read((char*)&magicCookie,sizeof(magicCookie)); if( !modelFile.good() ) return false; // TODO: Yuck! Why do we need to get here to figure out we're done? ASSERT( magicCookie == 1234, _L("Invalid model file.") ); modelFile.read((char*)&mStride, sizeof(mStride)); modelFile.read((char*)&mPaddingSize, sizeof(mPaddingSize)); // DWM TODO: What is this? modelFile.read((char*)&mTotalVerticesSizeInBytes, sizeof(mTotalVerticesSizeInBytes)); modelFile.read((char*)&mVertexCount, sizeof(mVertexCount)); modelFile.read((char*)&mTopology, sizeof(mTopology)); modelFile.read((char*)&mBboxCenter, sizeof(mBboxCenter)); modelFile.read((char*)&mBboxHalf, sizeof(mBboxHalf)); // read format descriptors modelFile.read((char*)&mFormatDescriptorCount, sizeof(mFormatDescriptorCount)); ASSERT( modelFile.good(), _L("Model file bad" ) ); mpElements = new CPUTVertexElementDesc[mFormatDescriptorCount]; for( UINT ii=0; ii #include "CPUT.h" class CPUTRenderParameters; class CPUTMaterial; class CPUTModel; //----------------------------------------------------------------------------- enum eCPUT_VERTEX_ELEMENT_SEMANTIC : UINT { CPUT_VERTEX_ELEMENT_UNDEFINED = 0, // Note 1 is missing (back compatibility) CPUT_VERTEX_ELEMENT_POSITON = 2, CPUT_VERTEX_ELEMENT_NORMAL = 3, CPUT_VERTEX_ELEMENT_TEXTURECOORD = 4, CPUT_VERTEX_ELEMENT_VERTEXCOLOR = 5, CPUT_VERTEX_ELEMENT_TANGENT = 6, CPUT_VERTEX_ELEMENT_BINORMAL = 7, }; const char *CPUT_VERTEX_ELEMENT_SEMANTIC_AS_STRING[]; //------------------------------------------------------------------------------ // These are hard coded, so you can add or deprecate, but not reuse them // If you change or add anything to this list, be sure to update the // CPUT_FILE_ELEMENT_TYPE_TO_CPUT_TYPE_CONVERT // struct below as well enum eCPUT_VERTEX_ELEMENT_TYPE : UINT { tMINTYPE = 1, tINT8, // 2 int = 1 byte tUINT8, // 3 UINT, __int8 = 1 byte tINT16, // 4 __int16 = 2 bytes tUINT16, // 5 unsigned __int16 = 2 bytes tINT32, // 6 __int32 = 4 bytes tUINT32, // 7 unsigned __int32 = 4 bytes tINT64, // 8 __int64 = 8 bytes tUINT64, // 9 unsigned __int64 = 8 bytes tBOOL, // 10 bool = 1 byte - '0' = false, '1' = true, same as stl bool i/o tCHAR, // 11 signed char = 1 byte tUCHAR, // 12 unsigned char = 1 byte tWCHAR, // 13 wchar_t = 2 bytes tFLOAT, // 14 float = 4 bytes tDOUBLE, // 15 double = 8 bytes // add new ones here tINVALID = 255 }; //------------------------------------------------------------------------------ // This map defines how the above eCPUT_VERTEX_ELEMENT_TYPE's map to internal // CPUT types. Be sure to update them at the same time. const CPUT_DATA_FORMAT_TYPE CPUT_FILE_ELEMENT_TYPE_TO_CPUT_TYPE_CONVERT[] = { CPUT_UNKNOWN, // 0 CPUT_UNKNOWN, // 1 CPUT_I8, // 2 int = 1 byte CPUT_U8, // 3 UINT, __int8 = 1 byte CPUT_I16, // 4 __int16 = 2 bytes CPUT_U16, // 5 unsigned __int16 = 2 bytes CPUT_I32, // 6 __int32 = 4 bytes CPUT_U32, // 7 unsigned __int32 = 4 bytes CPUT_I64, // 8 __int64 = 8 bytes CPUT_U64, // 9 unsigned __int64 = 8 bytes CPUT_BOOL, // 10 bool = 1 byte - '0' = false, '1' = true, same as stl bool i/o CPUT_CHAR, // 11 signed char = 1 byte CPUT_U8, // 12 unsigned char = 1 byte CPUT_U16, // 13 wchar_t = 2 bytes CPUT_F32, // 14 float = 4 bytes CPUT_DOUBLE, // 15 double = 8 bytes }; //----------------------------------------------------------------------------- enum eCPUT_MESH_TOPOLOGY : UINT { CPUT_TOPOLOGY_POINT_LIST = 1, CPUT_TOPOLOGY_INDEXED_LINE_LIST, CPUT_TOPOLOGY_INDEXED_LINE_STRIP, CPUT_TOPOLOGY_INDEXED_TRIANGLE_LIST, CPUT_TOPOLOGY_INDEXED_TRIANGLE_STRIP, CPUT_TOPOLOGY_INDEXED_TRIANGLE_FAN // TODO: What about non-indexed variants }; //----------------------------------------------------------------------------- struct CPUTVertexElementDesc { eCPUT_VERTEX_ELEMENT_SEMANTIC mVertexElementSemantic; // what is is , position, UV's, etc. eCPUT_VERTEX_ELEMENT_TYPE mVertexElementType; // what is the data type, floats, ints, etc. UINT mElementSizeInBytes; // # bytes of this element UINT mOffset; // what is the offset within the vertex data void Read(std::ifstream &meshFile); }; //----------------------------------------------------------------------------- struct CPUTRawMeshData { UINT mStride; // how big a "vertex" is UINT mVertexCount; void *mpVertices; UINT mIndexCount; UINT *mpIndices; // TODO: what about 16-bit indices? Use void*? UINT mFormatDescriptorCount; CPUTVertexElementDesc *mpElements; unsigned __int64 mTotalVerticesSizeInBytes; eCPUT_MESH_TOPOLOGY mTopology; float3 mBboxCenter; float3 mBboxHalf; eCPUT_VERTEX_ELEMENT_TYPE mIndexType; UINT mPaddingSize; CPUTRawMeshData(): mStride(0), mVertexCount(0), mpVertices(NULL), mIndexCount(0), mpIndices(NULL), mFormatDescriptorCount(0), mpElements(NULL), mTotalVerticesSizeInBytes(0), mTopology(CPUT_TOPOLOGY_INDEXED_TRIANGLE_LIST), mBboxCenter(0.0f), mIndexType(tUINT32), mBboxHalf(0.0f), mPaddingSize(0) { } ~CPUTRawMeshData() { delete[] mpVertices; delete[] mpElements; delete[] mpIndices; } void Allocate(__int32 numElements); bool Read(std::ifstream &mdlfile); }; //----------------------------------------------------------------------------- struct CPUTBufferInfo { const char *mpSemanticName; int mSemanticIndex; CPUT_DATA_FORMAT_TYPE mElementType; int mElementComponentCount; // e.g., a float 3 has 3 components UINT mElementSizeInBytes; UINT mOffset; int mElementCount; }; struct D3D11_MAPPED_SUBRESOURCE; //----------------------------------------------------------------------------- class CPUTMesh : public CPUTRefCount { protected: eCPUT_MESH_TOPOLOGY mMeshTopology; UINT mInstanceCount; public: CPUTMesh() : mInstanceCount(1) {} virtual ~CPUTMesh(){} // TODO: ? Change from virtual to #ifdef-controlled redirect to platform versions? // TODO: Use CPUT_MAPPED_SUBRESOURCE ?? virtual D3D11_MAPPED_SUBRESOURCE MapVertices( CPUTRenderParameters ¶ms, eCPUTMapType type, bool wait=true ) = 0; virtual D3D11_MAPPED_SUBRESOURCE MapIndices( CPUTRenderParameters ¶ms, eCPUTMapType type, bool wait=true ) = 0; virtual void UnmapVertices( CPUTRenderParameters ¶ms ) = 0; virtual void UnmapIndices( CPUTRenderParameters ¶ms ) = 0; virtual void SetMeshTopology(const eCPUT_MESH_TOPOLOGY meshTopology) { mMeshTopology = meshTopology; } virtual void BindVertexShaderLayout(CPUTMaterial *pMaterial, CPUTMaterial *pShadowCastMaterial) = 0; virtual CPUTResult CreateNativeResources( CPUTModel *pModel, UINT meshIdx, int vertexDataInfoArraySize, CPUTBufferInfo *pVertexInfo, void *pVertexData, CPUTBufferInfo *pIndexInfo, void *pIndex ) = 0; virtual void Draw(CPUTRenderParameters &renderParams, CPUTModel *pModel) = 0; virtual void DrawShadow(CPUTRenderParameters &renderParams, CPUTModel *pModel) = 0; void IncrementInstanceCount() { mInstanceCount++; } void DecrementInstanceCount() { mInstanceCount--; } }; #endif //__CPUTMESH_H__ ================================================ FILE: CPUT/CPUT/CPUTMeshDX11.cpp ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #include "CPUT_DX11.h" #include "CPUTMeshDX11.h" #include "CPUTMaterialDX11.h" #include "CPUTRenderParamsDX.h" #include "CPUTBufferDX11.h" //----------------------------------------------------------------------------- CPUTMeshDX11::CPUTMeshDX11(): mVertexStride(0), mVertexBufferOffset(0), mVertexCount(0), mpVertexBuffer(NULL), mpVertexBufferForSRVDX(NULL), mpVertexBufferForSRV(NULL), mpVertexView(NULL), mpStagingVertexBuffer(NULL), mVertexBufferMappedType(CPUT_MAP_UNDEFINED), mIndexBufferMappedType(CPUT_MAP_UNDEFINED), mIndexCount(0), mIndexBufferFormat(DXGI_FORMAT_UNKNOWN), mpIndexBuffer(NULL), mpStagingIndexBuffer(NULL), mpInputLayout(NULL), mpShadowInputLayout(NULL), mNumberOfInputLayoutElements(0), mpLayoutDescription(NULL) { } //----------------------------------------------------------------------------- CPUTMeshDX11::~CPUTMeshDX11() { ClearAllObjects(); } //----------------------------------------------------------------------------- void CPUTMeshDX11::ClearAllObjects() { SAFE_RELEASE(mpStagingIndexBuffer); SAFE_RELEASE(mpIndexBuffer); SAFE_RELEASE(mpStagingVertexBuffer); SAFE_RELEASE(mpVertexBuffer); SAFE_RELEASE(mpVertexBufferForSRVDX); SAFE_RELEASE(mpVertexBufferForSRV); SAFE_RELEASE(mpVertexView); SAFE_RELEASE(mpInputLayout); SAFE_RELEASE(mpShadowInputLayout); SAFE_DELETE_ARRAY(mpLayoutDescription); } // Create the DX vertex/index buffers and D3D11_INPUT_ELEMENT_DESC //----------------------------------------------------------------------------- CPUTResult CPUTMeshDX11::CreateNativeResources( CPUTModel *pModel, UINT meshIdx, int vertexDataInfoArraySize, CPUTBufferInfo *pVertexDataInfo, void *pVertexData, CPUTBufferInfo *pIndexDataInfo, void *pIndexData ){ CPUTResult result = CPUT_SUCCESS; HRESULT hr; ID3D11Device *pD3dDevice = CPUT_DX11::GetDevice(); // Release the layout, offset, stride, and vertex buffer structure objects ClearAllObjects(); // allocate the layout, offset, stride, and vertex buffer structure objects mpLayoutDescription = new D3D11_INPUT_ELEMENT_DESC[vertexDataInfoArraySize+1]; // Create the index buffer D3D11_SUBRESOURCE_DATA resourceData; if(NULL!=pIndexData) { mIndexCount = pIndexDataInfo->mElementCount; // set the data format info ZeroMemory( &mIndexBufferDesc, sizeof(mIndexBufferDesc) ); mIndexBufferDesc.Usage = D3D11_USAGE_DEFAULT; mIndexBufferDesc.ByteWidth = mIndexCount * pIndexDataInfo->mElementSizeInBytes; mIndexBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER; mIndexBufferDesc.CPUAccessFlags = 0; // default to no cpu access for speed // create the buffer ZeroMemory( &resourceData, sizeof(resourceData) ); resourceData.pSysMem = pIndexData; hr = pD3dDevice->CreateBuffer( &mIndexBufferDesc, &resourceData, &mpIndexBuffer ); ASSERT(!FAILED(hr), _L("Failed creating index buffer") ); CPUTSetDebugName( mpIndexBuffer, _L("Index buffer") ); // set the DX index buffer format mIndexBufferFormat = ConvertToDirectXFormat(pIndexDataInfo->mElementType, pIndexDataInfo->mElementComponentCount); } // set up data format info mVertexCount = pVertexDataInfo[0].mElementCount; ZeroMemory( &mVertexBufferDesc, sizeof(mVertexBufferDesc) ); mVertexBufferDesc.Usage = D3D11_USAGE_DEFAULT; // set the stride for one 'element' block of verts mVertexStride = pVertexDataInfo[vertexDataInfoArraySize-1].mOffset + pVertexDataInfo[vertexDataInfoArraySize-1].mElementSizeInBytes; // size in bytes of a single vertex block mVertexBufferDesc.ByteWidth = mVertexCount * mVertexStride; // size in bytes of entire buffer mVertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER; mVertexBufferDesc.CPUAccessFlags = 0; // default to no cpu access for speed // create the buffer ZeroMemory( &resourceData, sizeof(resourceData) ); resourceData.pSysMem = pVertexData; hr = pD3dDevice->CreateBuffer( &mVertexBufferDesc, &resourceData, &mpVertexBuffer ); ASSERT( !FAILED(hr), _L("Failed creating vertex buffer") ); CPUTSetDebugName( mpVertexBuffer, _L("Vertex buffer") ); // create the buffer for the shader resource view D3D11_BUFFER_DESC desc; ZeroMemory( &desc, sizeof(desc) ); desc.Usage = D3D11_USAGE_DEFAULT; // set the stride for one 'element' block of verts mVertexStride = pVertexDataInfo[vertexDataInfoArraySize-1].mOffset + pVertexDataInfo[vertexDataInfoArraySize-1].mElementSizeInBytes; // size in bytes of a single vertex block desc.ByteWidth = mVertexCount * mVertexStride; // size in bytes of entire buffer desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; desc.CPUAccessFlags = 0; desc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED; desc.StructureByteStride = mVertexStride; hr = pD3dDevice->CreateBuffer( &desc, &resourceData, &mpVertexBufferForSRVDX ); ASSERT( !FAILED(hr), _L("Failed creating vertex buffer for SRV") ); //CPUTSetDebugName( mpVertexBuffer, _L("Vertex buffer for SRV") ); // Create the shader resource view D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc; ZeroMemory( &srvDesc, sizeof(srvDesc) ); srvDesc.Format = DXGI_FORMAT_UNKNOWN; srvDesc.ViewDimension = D3D11_SRV_DIMENSION_BUFFER; srvDesc.Buffer.ElementOffset = 0; srvDesc.Buffer.NumElements = mVertexCount; hr = pD3dDevice->CreateShaderResourceView( mpVertexBufferForSRVDX, &srvDesc, &mpVertexView ); ASSERT( !FAILED(hr), _L("Failed creating vertex buffer SRV") ); cString name = _L("@VertexBuffer") + ptoc(pModel) + itoc(meshIdx); mpVertexBufferForSRV = new CPUTBufferDX11( name, mpVertexBufferForSRVDX, mpVertexView ); CPUTAssetLibrary::GetAssetLibrary()->AddBuffer( name, mpVertexBufferForSRV ); // build the layout object int currentByteOffset=0; mNumberOfInputLayoutElements = vertexDataInfoArraySize; for(int ii=0; iimpContext; pContext->IASetPrimitiveTopology( mD3DMeshTopology ); pContext->IASetVertexBuffers(0, 1, &mpVertexBuffer, &mVertexStride, &mVertexBufferOffset); pContext->IASetIndexBuffer(mpIndexBuffer, mIndexBufferFormat, 0); pContext->IASetInputLayout( pInputLayout ); pContext->DrawIndexed( mIndexCount, 0, 0 ); } // Sets the mesh topology, and converts it to it's DX format //----------------------------------------------------------------------------- void CPUTMeshDX11::SetMeshTopology(const eCPUT_MESH_TOPOLOGY meshTopology) { ASSERT( meshTopology > 0 && meshTopology <= 5, _L("") ); CPUTMesh::SetMeshTopology(meshTopology); // The CPUT enum has the same values as the D3D enum. Will likely need an xlation on OpenGL. mD3DMeshTopology = (D3D_PRIMITIVE_TOPOLOGY)meshTopology; } // Translate an internal CPUT data type into it's equivalent DirectX type //----------------------------------------------------------------------------- DXGI_FORMAT CPUTMeshDX11::ConvertToDirectXFormat(CPUT_DATA_FORMAT_TYPE dataFormatType, int componentCount) { ASSERT( componentCount>0 && componentCount<=4, _L("Invalid vertex element count.") ); switch( dataFormatType ) { case CPUT_F32: { const DXGI_FORMAT componentCountToFormat[4] = { DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_R32G32_FLOAT, DXGI_FORMAT_R32G32B32_FLOAT, DXGI_FORMAT_R32G32B32A32_FLOAT }; return componentCountToFormat[componentCount-1]; } case CPUT_U32: { const DXGI_FORMAT componentCountToFormat[4] = { DXGI_FORMAT_R32_UINT, DXGI_FORMAT_R32G32_UINT, DXGI_FORMAT_R32G32B32_UINT, DXGI_FORMAT_R32G32B32A32_UINT }; return componentCountToFormat[componentCount-1]; break; } case CPUT_U16: { ASSERT( 3 != componentCount, _L("Invalid vertex element count.") ); const DXGI_FORMAT componentCountToFormat[4] = { DXGI_FORMAT_R16_UINT, DXGI_FORMAT_R16G16_UINT, DXGI_FORMAT_UNKNOWN, // Count of 3 is invalid for 16-bit type DXGI_FORMAT_R16G16B16A16_UINT }; return componentCountToFormat[componentCount-1]; } default: { // todo: add all the other data types you want to support ASSERT(0,_L("Unsupported vertex element type")); } return DXGI_FORMAT_UNKNOWN; } } // CPUTMeshDX11::ConvertToDirectXFormat() // Finds a layout that matches the materials vertex shader and the mesh's // vertex buffer layout by using the InputLayoutCache //----------------------------------------------------------------------------- void CPUTMeshDX11::BindVertexShaderLayout( CPUTMaterial *pMaterial, CPUTMaterial *pShadowCastMaterial ) { ID3D11Device *pDevice = CPUT_DX11::GetDevice(); if( pMaterial ) { // Get the vertex layout for this shader/format comb // If already exists, then GetLayout() returns the existing layout for reuse. CPUTVertexShaderDX11 *pVertexShader = ((CPUTMaterialDX11*)pMaterial)->GetVertexShader(); SAFE_RELEASE(mpInputLayout); CPUTInputLayoutCacheDX11::GetInputLayoutCache()->GetLayout(pDevice, mpLayoutDescription, pVertexShader, &mpInputLayout); } if( pShadowCastMaterial ) { CPUTVertexShaderDX11 *pVertexShader = ((CPUTMaterialDX11*)pShadowCastMaterial)->GetVertexShader(); SAFE_RELEASE(mpShadowInputLayout); CPUTInputLayoutCacheDX11::GetInputLayoutCache()->GetLayout(pDevice, mpLayoutDescription, pVertexShader, &mpShadowInputLayout); } } //----------------------------------------------------------------------------- D3D11_MAPPED_SUBRESOURCE CPUTMeshDX11::Map( UINT count, ID3D11Buffer *pBuffer, D3D11_BUFFER_DESC &bufferDesc, ID3D11Buffer **pStagingBuffer, eCPUTMapType *pMappedType, CPUTRenderParameters ¶ms, eCPUTMapType type, bool wait ) { // Mapping for DISCARD requires dynamic buffer. Create dynamic copy? // Could easily provide input flag. But, where would we specify? Don't like specifying in the .set file // Because mapping is something the application wants to do - it isn't inherent in the data. // Could do Clone() and pass dynamic flag to that. // But, then we have two. Could always delete the other. // Could support programatic flag - apply to all loaded models in the .set // Could support programatic flag on model. Load model first, then load set. // For now, simply support CopyResource mechanism. HRESULT hr; ID3D11Device *pD3dDevice = CPUT_DX11::GetDevice(); CPUTRenderParametersDX *pParamsDX11 = (CPUTRenderParametersDX*)¶ms; ID3D11DeviceContext *pContext = pParamsDX11->mpContext; if( !*pStagingBuffer ) { D3D11_BUFFER_DESC desc = bufferDesc; // First time. Create the staging resource desc.Usage = D3D11_USAGE_STAGING; switch( type ) { case CPUT_MAP_READ: desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; desc.BindFlags = 0; break; case CPUT_MAP_READ_WRITE: desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE; desc.BindFlags = 0; break; case CPUT_MAP_WRITE: case CPUT_MAP_WRITE_DISCARD: case CPUT_MAP_NO_OVERWRITE: desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; desc.BindFlags = 0; break; }; hr = pD3dDevice->CreateBuffer( &desc, NULL, pStagingBuffer ); ASSERT( SUCCEEDED(hr), _L("Failed to create staging buffer") ); CPUTSetDebugName( *pStagingBuffer, _L("Mesh Staging buffer") ); } else { ASSERT( *pMappedType == type, _L("Mapping with a different CPU access than creation parameter.") ); } D3D11_MAPPED_SUBRESOURCE info; switch( type ) { case CPUT_MAP_READ: case CPUT_MAP_READ_WRITE: // TODO: Copying and immediately mapping probably introduces a stall. // Expose the copy externally? // TODO: copy only if vb has changed? // Copy only first time? // Copy the GPU version before we read from it. pContext->CopyResource( *pStagingBuffer, pBuffer ); break; }; hr = pContext->Map( *pStagingBuffer, wait ? 0 : D3D11_MAP_FLAG_DO_NOT_WAIT, (D3D11_MAP)type, 0, &info ); *pMappedType = type; return info; } // CPUTMeshDX11::Map() //----------------------------------------------------------------------------- void CPUTMeshDX11::Unmap( ID3D11Buffer *pBuffer, ID3D11Buffer *pStagingBuffer, eCPUTMapType *pMappedType, CPUTRenderParameters ¶ms ) { ASSERT( *pMappedType != CPUT_MAP_UNDEFINED, _L("Can't unmap a buffer that isn't mapped.") ); CPUTRenderParametersDX *pParamsDX11 = (CPUTRenderParametersDX*)¶ms; ID3D11DeviceContext *pContext = pParamsDX11->mpContext; pContext->Unmap( pStagingBuffer, 0 ); // If we were mapped for write, then copy staging buffer to GPU switch( *pMappedType ) { case CPUT_MAP_READ: break; case CPUT_MAP_READ_WRITE: case CPUT_MAP_WRITE: case CPUT_MAP_WRITE_DISCARD: case CPUT_MAP_NO_OVERWRITE: pContext->CopyResource( pBuffer, pStagingBuffer ); break; }; // *pMappedType = CPUT_MAP_UNDEFINED; // TODO: Just commented this out. Otherwise, assert in Map() fails. Verify. } // CPUTMeshDX11::Unmap() //----------------------------------------------------------------------------- D3D11_MAPPED_SUBRESOURCE CPUTMeshDX11::MapVertices( CPUTRenderParameters ¶ms, eCPUTMapType type, bool wait ) { return Map( mVertexCount, mpVertexBuffer, mVertexBufferDesc, &mpStagingVertexBuffer, &mVertexBufferMappedType, params, type, wait ); } //----------------------------------------------------------------------------- D3D11_MAPPED_SUBRESOURCE CPUTMeshDX11::MapIndices( CPUTRenderParameters ¶ms, eCPUTMapType type, bool wait ) { return Map( mIndexCount, mpIndexBuffer, mIndexBufferDesc, &mpStagingIndexBuffer, &mIndexBufferMappedType, params, type, wait ); } //----------------------------------------------------------------------------- void CPUTMeshDX11::UnmapVertices( CPUTRenderParameters ¶ms ) { Unmap( mpVertexBuffer, mpStagingVertexBuffer, &mVertexBufferMappedType, params ); } //----------------------------------------------------------------------------- void CPUTMeshDX11::UnmapIndices( CPUTRenderParameters ¶ms ) { Unmap( mpIndexBuffer, mpStagingIndexBuffer, &mIndexBufferMappedType, params ); } ================================================ FILE: CPUT/CPUT/CPUTMeshDX11.h ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #ifndef __CPUTMESHDX11_H__ #define __CPUTMESHDX11_H__ #pragma once #include "CPUTMesh.h" #include "CPUTRenderParamsDX.h" #include "CPUTInputLayoutCacheDX11.h" #include "CPUT.h" #include "CPUTOSServicesWin.h" class CPUTMaterial; class CPUTMaterialDX11; class CPUTBufferDX11; class CPUTComputeShaderDX11; class CPUTModelDX11; //----------------------------------------------------------------------------- class CPUTMeshDX11 : public CPUTMesh {protected: D3D_PRIMITIVE_TOPOLOGY mD3DMeshTopology; D3D11_INPUT_ELEMENT_DESC *mpLayoutDescription; int mNumberOfInputLayoutElements; ID3D11InputLayout *mpInputLayout; ID3D11InputLayout *mpShadowInputLayout; UINT mVertexStride; D3D11_BUFFER_DESC mVertexBufferDesc; UINT mVertexBufferOffset; UINT mVertexCount; ID3D11Buffer *mpVertexBuffer; ID3D11Buffer *mpStagingVertexBuffer; eCPUTMapType mVertexBufferMappedType; ID3D11Buffer *mpVertexBufferForSRVDX; // Need SRV, but _real_ DX won't allow for _real_ VB ID3D11ShaderResourceView *mpVertexView; CPUTBufferDX11 *mpVertexBufferForSRV; UINT mIndexCount; DXGI_FORMAT mIndexBufferFormat; ID3D11Buffer *mpIndexBuffer; D3D11_BUFFER_DESC mIndexBufferDesc; ID3D11Buffer *mpStagingIndexBuffer; eCPUTMapType mIndexBufferMappedType; public: CPUTMeshDX11(); virtual ~CPUTMeshDX11(); D3D11_INPUT_ELEMENT_DESC *GetLayoutDescription() { return mpLayoutDescription; } ID3D11Buffer *GetIndexBuffer() { return mpIndexBuffer; } ID3D11Buffer *GetVertexBuffer() { return mpVertexBuffer; } void SetMeshTopology(const eCPUT_MESH_TOPOLOGY eDrawTopology); CPUTResult CreateNativeResources( CPUTModel *pModel, UINT meshIdx, int vertexDataInfoArraySize, CPUTBufferInfo *pVertexInfo, void *pVertexData, CPUTBufferInfo *pIndexInfo, void *pIndex ); void BindVertexShaderLayout(CPUTMaterial *pMaterial, CPUTMaterial *pShadowCastMaterial); void Draw(CPUTRenderParameters &renderParams, CPUTModel *pModel) { Draw(renderParams, pModel, mpInputLayout);} void DrawShadow(CPUTRenderParameters &renderParams, CPUTModel *pModel) { Draw(renderParams, pModel, mpShadowInputLayout);} void Draw(CPUTRenderParameters &renderParams, CPUTModel *pModel, ID3D11InputLayout *pLayout); D3D11_MAPPED_SUBRESOURCE MapVertices( CPUTRenderParameters ¶ms, eCPUTMapType type, bool wait=true ); D3D11_MAPPED_SUBRESOURCE MapIndices( CPUTRenderParameters ¶ms, eCPUTMapType type, bool wait=true ); void UnmapVertices( CPUTRenderParameters ¶ms ); void UnmapIndices( CPUTRenderParameters ¶ms ); UINT GetTriangleCount() { return mIndexCount/3; } UINT GetVertexCount() { return mVertexCount; } UINT GetIndexCount() { return mIndexCount; } protected: // Mapping vertex and index buffers is very similar. This internal function does both D3D11_MAPPED_SUBRESOURCE Map( UINT count, ID3D11Buffer *pBuffer, D3D11_BUFFER_DESC &bufferDesc, ID3D11Buffer **pStagingBuffer, eCPUTMapType *pMappedType, CPUTRenderParameters ¶ms, eCPUTMapType type, bool wait = true ); void Unmap( ID3D11Buffer *pBuffer, ID3D11Buffer *pStagingBuffer, eCPUTMapType *pMappedType, CPUTRenderParameters ¶ms ); void ClearAllObjects(); // delete all allocations held by this object DXGI_FORMAT ConvertToDirectXFormat(CPUT_DATA_FORMAT_TYPE DataFormatElementType, int NumberDataFormatElements); }; #endif // __CPUTMESHDX11_H__ ================================================ FILE: CPUT/CPUT/CPUTModel.cpp ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #include "CPUTModel.h" #include "CPUTMaterial.h" #include "CPUTMesh.h" #include "CPUTAssetLibrary.h" //----------------------------------------------------------------------------- CPUTModel::~CPUTModel() { // TODO: Do we need a shadowCastMaterial per mesh? What happens if the meshes have different layouts? SAFE_RELEASE(mpShadowCastMaterial); for( UINT ii=0; iiDecrementInstanceCount(); } SAFE_RELEASE(mpMesh[ii]); HEAPCHECK; } SAFE_RELEASE(mpBoundingBoxMesh); SAFE_RELEASE(mpBoundingBoxMaterial); SAFE_DELETE_ARRAY(mpMaterial); SAFE_DELETE_ARRAY(mpMesh); } //----------------------------------------------------------------------------- void CPUTModel::GetBoundsObjectSpace(float3 *pCenter, float3 *pHalf) { *pCenter = mBoundingBoxCenterObjectSpace; *pHalf = mBoundingBoxHalfObjectSpace; } //----------------------------------------------------------------------------- void CPUTModel::GetBoundsWorldSpace(float3 *pCenter, float3 *pHalf) { *pCenter = mBoundingBoxCenterWorldSpace; *pHalf = mBoundingBoxHalfWorldSpace; } //----------------------------------------------------------------------------- void CPUTModel::UpdateBoundsWorldSpace() { // If an object is rigid, then it's object-space bounding box doesn't change. // However, if it moves, then it's world-space bounding box does change. // Call this function when the model moves float4x4 *pWorld = GetWorldMatrix(); float4 center = float4(mBoundingBoxCenterObjectSpace, 1.0f); // W = 1 because we want the xlation (i.e., center is a position) float4 half = float4(mBoundingBoxHalfObjectSpace, 0.0f); // W = 0 because we don't want xlation (i.e., half is a direction) // TODO: optimize this float4 positions[8] = { center + float4( 1.0f, 1.0f, 1.0f, 0.0f ) * half, center + float4( 1.0f, 1.0f,-1.0f, 0.0f ) * half, center + float4( 1.0f,-1.0f, 1.0f, 0.0f ) * half, center + float4( 1.0f,-1.0f,-1.0f, 0.0f ) * half, center + float4(-1.0f, 1.0f, 1.0f, 0.0f ) * half, center + float4(-1.0f, 1.0f,-1.0f, 0.0f ) * half, center + float4(-1.0f,-1.0f, 1.0f, 0.0f ) * half, center + float4(-1.0f,-1.0f,-1.0f, 0.0f ) * half }; float4 minPosition( FLT_MAX, FLT_MAX, FLT_MAX, 1.0f ); float4 maxPosition(-FLT_MAX, -FLT_MAX, -FLT_MAX, 1.0f ); for( UINT ii=0; ii<8; ii++ ) { float4 position = positions[ii] * *pWorld; minPosition = Min( minPosition, position ); maxPosition = Max( maxPosition, position ); } mBoundingBoxCenterWorldSpace = (maxPosition + minPosition) * 0.5f; mBoundingBoxHalfWorldSpace = (maxPosition - minPosition) * 0.5f; } //----------------------------------------------------------------------------- CPUTResult CPUTModel::LoadModelPayload(const cString &File) { CPUTResult result = CPUT_SUCCESS; std::ifstream file(File.c_str(), std::ios::in | std::ios::binary); ASSERT( !file.fail(), _L("CPUTModelDX11::LoadModelPayload() - Could not find binary model file: ") + File ); // set up for mesh creation loop UINT meshIndex = 0; while(file.good() && !file.eof()) { // TODO: rearrange while() to avoid if(eof). Should perform only one branch per loop iteration, not two CPUTRawMeshData vertexFormatDesc; vertexFormatDesc.Read(file); if(file.eof()) { // TODO: Wtf? Why would we get here? We check eof at the top of loop. If it isn't eof there, why is it eof here? break; } ASSERT( meshIndex < mMeshCount, _L("Actual mesh count doesn't match stated mesh count")); // create the mesh. CPUTMesh *pMesh = mpMesh[meshIndex]; // always a triangle list (at this point) pMesh->SetMeshTopology(CPUT_TOPOLOGY_INDEXED_TRIANGLE_LIST); // get number of data blocks in the vertex element (pos,norm,uv,etc) // YUCK! TODO: Use fixed-size array of elements CPUTBufferInfo *pVertexElementInfo = new CPUTBufferInfo[vertexFormatDesc.mFormatDescriptorCount]; // pMesh->SetBounds(vertexFormatDesc.mBboxCenter, vertexFormatDesc.mBboxHalf); // running count of each type of element int positionStreamCount=0; int normalStreamCount=0; int texCoordStreamCount=0; int tangentStreamCount=0; int binormalStreamCount=0; int colorStreamCount=0; int RunningOffset = 0; for(UINT ii=0; iimElementCount && indexDataInfo.mElementCount ) { result = pMesh->CreateNativeResources( this, meshIndex, vertexFormatDesc.mFormatDescriptorCount, pVertexElementInfo, (void*)vertexFormatDesc.mpVertices, &indexDataInfo, &vertexFormatDesc.mpIndices[0] ); if(CPUTFAILED(result)) { return result; } } delete [] pVertexElementInfo; pVertexElementInfo = NULL; ++meshIndex; } ASSERT( file.eof(), _L("") ); // close file file.close(); return result; } // Set the material associated with this mesh and create/re-use a //----------------------------------------------------------------------------- void CPUTModel::SetMaterial(UINT ii, CPUTMaterial *pMaterial) { // TODO: ASSSERT that ii is in range // release old material pointer SAFE_RELEASE( mpMaterial[ii] ); mpMaterial[ii] = pMaterial; if(mpMaterial[ii]) { mpMaterial[ii]->AddRef(); } } #ifdef SUPPORT_DRAWING_BOUNDING_BOXES // TODO: We create a native mesh below. Best way to do this? Move the whole thing to CPUTModelDX11? Add CPUTMesh::CreateNativeMesh() or equiv? // Note that we only need one of these. We don't need to re-create it for every model. #include "CPUTMeshDX11.h" //----------------------------------------------------------------------------- void CPUTModel::CreateBoundingBoxMesh() { CPUTResult result = CPUT_SUCCESS; float3 pVertices[8] = { float3( 1.0f, 1.0f, 1.0f ), // 0 float3( 1.0f, 1.0f, -1.0f ), // 1 float3( -1.0f, 1.0f, 1.0f ), // 2 float3( -1.0f, 1.0f, -1.0f ), // 3 float3( 1.0f, -1.0f, 1.0f ), // 4 float3( 1.0f, -1.0f, -1.0f ), // 5 float3( -1.0f, -1.0f, 1.0f ), // 6 float3( -1.0f, -1.0f, -1.0f ) // 7 }; USHORT pIndices[24] = { 0,1, 1,3, 3,2, 2,0, // Top 4,5, 5,7, 7,6, 6,4, // Bottom 0,4, 1,5, 2,6, 3,7 // Verticals }; CPUTVertexElementDesc pVertexElements[] = { { CPUT_VERTEX_ELEMENT_POSITON, tFLOAT, 12, 0 }, }; CPUTMesh *pMesh = mpBoundingBoxMesh = new CPUTMeshDX11(); pMesh->SetMeshTopology(CPUT_TOPOLOGY_INDEXED_LINE_LIST); CPUTBufferInfo vertexElementInfo; vertexElementInfo.mpSemanticName = "POSITION"; vertexElementInfo.mSemanticIndex = 0; vertexElementInfo.mElementType = CPUT_F32; vertexElementInfo.mElementComponentCount = 3; vertexElementInfo.mElementSizeInBytes = 12; vertexElementInfo.mElementCount = 8; vertexElementInfo.mOffset = 0; CPUTBufferInfo indexDataInfo; indexDataInfo.mElementType = CPUT_U16; indexDataInfo.mElementComponentCount = 1; indexDataInfo.mElementSizeInBytes = sizeof(UINT16); indexDataInfo.mElementCount = 24; // 12 lines, 2 verts each indexDataInfo.mOffset = 0; indexDataInfo.mSemanticIndex = 0; indexDataInfo.mpSemanticName = NULL; result = pMesh->CreateNativeResources( 1, // vertexFormatDesc.mFormatDescriptorCount, &vertexElementInfo, pVertices, // (void*)vertexFormatDesc.mpVertices, &indexDataInfo, pIndices // &vertexFormatDesc.mpIndices[0] ); ASSERT( CPUTSUCCESS(result), _L("Failed building bounding box mesh") ); mpBoundingBoxMaterial = CPUTAssetLibrary::GetAssetLibrary()->GetMaterial(_L("BoundingBox") ); pMesh->BindVertexShaderLayout( mpBoundingBoxMaterial ); } #endif ================================================ FILE: CPUT/CPUT/CPUTModel.h ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #ifndef __CPUTMODEL_H__ #define __CPUTMODEL_H__ // Define the following to support drawing bounding boxes. // Note that you also need bounding-box materials and shaders. TODO: Include them in the exe. // #define SUPPORT_DRAWING_BOUNDING_BOXES 1 #include "CPUTRenderNode.h" #include "CPUTMath.h" #include "CPUTConfigBlock.h" #include "CPUTMesh.h" class CPUTMaterial; class CPUTMesh; //----------------------------------------------------------------------------- class CPUTModel : public CPUTRenderNode { protected: CPUTMesh **mpMesh; CPUTMaterial **mpMaterial; CPUTMaterial *mpShadowCastMaterial; UINT mMeshCount; bool mIsRenderable; float3 mBoundingBoxCenterObjectSpace; float3 mBoundingBoxHalfObjectSpace; float3 mBoundingBoxCenterWorldSpace; float3 mBoundingBoxHalfWorldSpace; CPUTMesh *mpBoundingBoxMesh; CPUTMaterial *mpBoundingBoxMaterial; public: CPUTModel(): mMeshCount(0), mpMesh(NULL), mIsRenderable(true), mBoundingBoxCenterObjectSpace(0.0f), mBoundingBoxHalfObjectSpace(0.0f), mBoundingBoxCenterWorldSpace(0.0f), mBoundingBoxHalfWorldSpace(0.0f), mpBoundingBoxMesh(NULL), mpBoundingBoxMaterial(NULL), mpShadowCastMaterial(NULL) {} virtual ~CPUTModel(); bool IsRenderable() { return mIsRenderable; } void SetRenderable(bool isRenderable) { mIsRenderable = isRenderable; } virtual bool IsModel() { return true; } void GetBoundsObjectSpace(float3 *pCenter, float3 *pHalf); void GetBoundsWorldSpace(float3 *pCenter, float3 *pHalf); void UpdateBoundsWorldSpace(); int GetMeshCount() const { return mMeshCount; } CPUTMesh *GetMesh( UINT ii ) { return mpMesh[ii]; } virtual CPUTResult LoadModel(CPUTConfigBlock *pBlock, int *pParentID, CPUTModel *pMasterModel=NULL) = 0; CPUTResult LoadModelPayload(const cString &File); virtual void SetMaterial(UINT ii, CPUTMaterial *pMaterial); #ifdef SUPPORT_DRAWING_BOUNDING_BOXES virtual void DrawBoundingBox(CPUTRenderParameters &renderParams) = 0; void CreateBoundingBoxMesh(); #endif void GetBoundingBoxRecursive( float3 *pCenter, float3 *pHalf) { if( *pHalf == float3(0.0f) ) { *pCenter = mBoundingBoxCenterWorldSpace; *pHalf = mBoundingBoxHalfWorldSpace; } else { float3 minExtent = *pCenter - *pHalf; float3 maxExtent = *pCenter + *pHalf; minExtent = Min( (mBoundingBoxCenterWorldSpace - mBoundingBoxHalfWorldSpace), minExtent ); maxExtent = Max( (mBoundingBoxCenterWorldSpace + mBoundingBoxHalfWorldSpace), maxExtent ); *pCenter = (minExtent + maxExtent) * 0.5f; *pHalf = (maxExtent - minExtent) * 0.5f; } if(mpChild) { mpChild->GetBoundingBoxRecursive( pCenter, pHalf ); } if(mpSibling) { mpSibling->GetBoundingBoxRecursive( pCenter, pHalf ); } } }; #endif // __CPUTMODEL_H__ ================================================ FILE: CPUT/CPUT/CPUTModelDX11.cpp ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #include "CPUTModelDX11.h" #include "CPUTMaterialDX11.h" #include "CPUTRenderParamsDX.h" #include "CPUTFrustum.h" #include "CPUTTextureDX11.h" #include "CPUTBufferDX11.h" // Return the mesh at the given index (cast to the GFX api version of CPUTMeshDX11) //----------------------------------------------------------------------------- CPUTMeshDX11* CPUTModelDX11::GetMesh(const UINT index) const { return ( 0==mMeshCount || index > mMeshCount) ? NULL : (CPUTMeshDX11*)mpMesh[index]; } float3 gLightDir = float3(0.7f, -0.5f, -0.1f); // Set the render state before drawing this object //----------------------------------------------------------------------------- void CPUTModelDX11::SetRenderStates(CPUTRenderParameters &renderParams) { // TODO: need to update the constant buffer only when the model moves. // But, requires individual, per-model constant buffers ID3D11DeviceContext *pContext = ((CPUTRenderParametersDX*)&renderParams)->mpContext; CPUTModelConstantBuffer *pCb; // update parameters of constant buffer D3D11_MAPPED_SUBRESOURCE mapInfo; pContext->Map( mpModelConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapInfo ); { // TODO: remove construction of XMM type XMMATRIX world((float*)GetWorldMatrix()); XMVECTOR determinant = XMMatrixDeterminant(world); CPUTCamera *pCamera = gpSample->GetCamera(); XMMATRIX view((float*)pCamera->GetViewMatrix()); XMMATRIX projection((float*)pCamera->GetProjectionMatrix()); float *pCameraPos = (float*)&pCamera->GetPosition(); XMVECTOR cameraPos = XMLoadFloat3(&XMFLOAT3( pCameraPos[0], pCameraPos[1], pCameraPos[2] )); pCb = (CPUTModelConstantBuffer*)mapInfo.pData; pCb->World = world; pCb->ViewProjection = view *projection; pCb->WorldViewProjection = world *pCb->ViewProjection; pCb->InverseWorld = XMMatrixInverse(&determinant, XMMatrixTranspose(world)); // pCb->LightDirection = XMVector3Transform(gLightDir, pCb->InverseWorld ); // pCb->EyePosition = XMVector3Transform(cameraPos, pCb->InverseWorld ); // TODO: Tell the lights to set their render states XMVECTOR lightDirection = XMLoadFloat3(&XMFLOAT3( gLightDir.x, gLightDir.y, gLightDir.z )); pCb->LightDirection = XMVector3Normalize(lightDirection); pCb->EyePosition = cameraPos; float *bbCWS = (float*)&mBoundingBoxCenterWorldSpace; float *bbHWS = (float*)&mBoundingBoxHalfWorldSpace; float *bbCOS = (float*)&mBoundingBoxCenterObjectSpace; float *bbHOS = (float*)&mBoundingBoxHalfObjectSpace; pCb->BoundingBoxCenterWorldSpace = XMLoadFloat3(&XMFLOAT3( bbCWS[0], bbCWS[1], bbCWS[2] )); ; pCb->BoundingBoxHalfWorldSpace = XMLoadFloat3(&XMFLOAT3( bbHWS[0], bbHWS[1], bbHWS[2] )); ; pCb->BoundingBoxCenterObjectSpace = XMLoadFloat3(&XMFLOAT3( bbCOS[0], bbCOS[1], bbCOS[2] )); ; pCb->BoundingBoxHalfObjectSpace = XMLoadFloat3(&XMFLOAT3( bbHOS[0], bbHOS[1], bbHOS[2] )); ; // Shadow camera XMMATRIX shadowView, shadowProjection; CPUTCamera *pShadowCamera = gpSample->GetShadowCamera(); if( pShadowCamera ) { shadowView = XMMATRIX((float*)pShadowCamera->GetViewMatrix()); shadowProjection = XMMATRIX((float*)pShadowCamera->GetProjectionMatrix()); pCb->LightWorldViewProjection = world * shadowView * shadowProjection; } } pContext->Unmap(mpModelConstantBuffer,0); } // Render - render this model (only) //----------------------------------------------------------------------------- void CPUTModelDX11::Render(CPUTRenderParameters &renderParams) { CPUTRenderParametersDX *pParams = (CPUTRenderParametersDX*)&renderParams; CPUTCamera *pCamera = pParams->mpCamera; #ifdef SUPPORT_DRAWING_BOUNDING_BOXES if( renderParams.mShowBoundingBoxes && (!pCamera || pCamera->mFrustum.IsVisible( mBoundingBoxCenterWorldSpace, mBoundingBoxHalfWorldSpace ))) { DrawBoundingBox( renderParams ); } #endif if( !renderParams.mDrawModels ) { return; } // TODO: add world-space bounding box to model so we don't need to do that work every frame if( !pParams->mRenderOnlyVisibleModels || !pCamera || pCamera->mFrustum.IsVisible( mBoundingBoxCenterWorldSpace, mBoundingBoxHalfWorldSpace ) ) { // loop over all meshes in this model and draw them for(UINT ii=0; iiSetRenderStates(renderParams); // We would like to set the model's render states only once (and then iterate over materials) // But, the material resource lists leave holes for per-model resources (e.g., constant buffers) // We need to 'fixup' the bound resources. The material sets some to 0, and the model overwrites them with the correct values. SetRenderStates(renderParams); // Potentially need to use a different vertex-layout object! CPUTVertexShaderDX11 *pVertexShader = pMaterial->GetVertexShader(); ((CPUTMeshDX11*)mpMesh[ii])->Draw(renderParams, this); } } } // Render - render this model (only) //----------------------------------------------------------------------------- void CPUTModelDX11::RenderShadow(CPUTRenderParameters &renderParams) { CPUTRenderParametersDX *pParams = (CPUTRenderParametersDX*)&renderParams; CPUTCamera *pCamera = pParams->mpCamera; #ifdef SUPPORT_DRAWING_BOUNDING_BOXES if( renderParams.mShowBoundingBoxes && (!pCamera || pCamera->mFrustum.IsVisible( mBoundingBoxCenterWorldSpace, mBoundingBoxHalfWorldSpace ))) { DrawBoundingBox( renderParams ); } #endif if( !renderParams.mDrawModels ) { return; } // TODO: add world-space bounding box to model so we don't need to do that work every frame if( !pParams->mRenderOnlyVisibleModels || !pCamera || pCamera->mFrustum.IsVisible( mBoundingBoxCenterWorldSpace, mBoundingBoxHalfWorldSpace ) ) { // loop over all meshes in this model and draw them for(UINT ii=0; iiSetRenderStates(renderParams); // We would like to set the model's render states only once (and then iterate over materials) // But, the material resource lists leave holes for per-model resources (e.g., constant buffers) // We need to 'fixup' the bound resources. The material sets some to 0, and the model overwrites them with the correct values. SetRenderStates(renderParams); // Potentially need to use a different vertex-layout object! CPUTVertexShaderDX11 *pVertexShader = pMaterial->GetVertexShader(); ((CPUTMeshDX11*)mpMesh[ii])->DrawShadow(renderParams, this); } } } #ifdef SUPPORT_DRAWING_BOUNDING_BOXES //----------------------------------------------------------------------------- void CPUTModelDX11::DrawBoundingBox(CPUTRenderParameters &renderParams) { SetRenderStates(renderParams); CPUTMaterialDX11 *pMaterial = (CPUTMaterialDX11*)mpBoundingBoxMaterial; mpBoundingBoxMaterial->SetRenderStates(renderParams); ((CPUTMeshDX11*)mpBoundingBoxMesh)->Draw(renderParams, this); } #endif // Load the set file definition of this object // 1. Parse the block of name/parent/transform info for model block // 2. Load the model's binary payload (i.e., the meshes) // 3. Assert the # of meshes matches # of materials // 4. Load each mesh's material //----------------------------------------------------------------------------- CPUTResult CPUTModelDX11::LoadModel(CPUTConfigBlock *pBlock, int *pParentID, CPUTModel *pMasterModel) { CPUTResult result = CPUT_SUCCESS; CPUTAssetLibraryDX11 *pAssetLibrary = (CPUTAssetLibraryDX11*)CPUTAssetLibrary::GetAssetLibrary(); cString modelSuffix = ptoc(this); // set the model's name mName = pBlock->GetValueByName(_L("name"))->ValueAsString(); mName = mName + _L(".mdl"); // resolve the full path name cString modelLocation; cString resolvedPathAndFile; modelLocation = ((CPUTAssetLibraryDX11*)CPUTAssetLibrary::GetAssetLibrary())->GetModelDirectory(); modelLocation = modelLocation+mName; CPUTOSServices::GetOSServices()->ResolveAbsolutePathAndFilename(modelLocation, &resolvedPathAndFile); // Get the parent ID. Note: the caller will use this to set the parent. *pParentID = pBlock->GetValueByName(_L("parent"))->ValueAsInt(); LoadParentMatrixFromParameterBlock( pBlock ); // Get the bounding box information float3 center(0.0f), half(0.0f); pBlock->GetValueByName(_L("BoundingBoxCenter"))->ValueAsFloatArray(center.f, 3); pBlock->GetValueByName(_L("BoundingBoxHalf"))->ValueAsFloatArray(half.f, 3); mBoundingBoxCenterObjectSpace = center; mBoundingBoxHalfObjectSpace = half; // the # of meshes in the binary file better match the number of meshes in the .set file definition mMeshCount = pBlock->GetValueByName(_L("meshcount"))->ValueAsInt(); mpMesh = new CPUTMesh*[mMeshCount]; mpMaterial = new CPUTMaterial*[mMeshCount]; memset( mpMaterial, 0, mMeshCount * sizeof(CPUTMaterial*) ); // get the material names, load them, and match them up with each mesh cString materialName; char pNumber[4]; cString materialValueName; CPUTModelDX11 *pMasterModelDX = (CPUTModelDX11*)pMasterModel; for(UINT ii=0; iimpMesh[ii]; mpMesh[ii]->AddRef(); } else { mpMesh[ii] = new CPUTMeshDX11(); } } if( !pMasterModelDX ) { // Not a clone/instance. So, load the model's binary payload (i.e., vertex and index buffers) // TODO: Change to use GetModel() result = LoadModelPayload(resolvedPathAndFile); ASSERT( CPUTSUCCESS(result), _L("Failed loading model") ); } // Create the model constant buffer. HRESULT hr; D3D11_BUFFER_DESC bd = {0}; bd.ByteWidth = sizeof(CPUTModelConstantBuffer); bd.BindFlags = D3D11_BIND_CONSTANT_BUFFER; bd.Usage = D3D11_USAGE_DYNAMIC; bd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; hr = (CPUT_DX11::GetDevice())->CreateBuffer( &bd, NULL, &mpModelConstantBuffer ); ASSERT( !FAILED( hr ), _L("Error creating constant buffer.") ); CPUTSetDebugName( mpModelConstantBuffer, _L("Model Constant buffer") ); cString pModelSuffix = ptoc(this); cString name = _L("#cbPerModelValues") + pModelSuffix; CPUTBufferDX11 *pBuffer = new CPUTBufferDX11(name, mpModelConstantBuffer); pAssetLibrary->AddConstantBuffer( name, pBuffer ); pBuffer->Release(); // We're done with it. We added it to the library. Release our reference. cString assetSetDirectoryName = pAssetLibrary->GetAssetSetDirectoryName(); cString modelDirectory = pAssetLibrary->GetModelDirectory(); cString materialDirectory = pAssetLibrary->GetMaterialDirectory(); cString textureDirectory = pAssetLibrary->GetTextureDirectory(); cString shaderDirectory = pAssetLibrary->GetShaderDirectory(); cString fontDirectory = pAssetLibrary->GetFontDirectory(); cString up2MediaDirName = assetSetDirectoryName + _L("..\\..\\"); pAssetLibrary->SetMediaDirectoryName( up2MediaDirName ); mpShadowCastMaterial = pAssetLibrary->GetMaterial( _L("shadowCast"), false, modelSuffix ); pAssetLibrary->SetAssetSetDirectoryName( assetSetDirectoryName ); pAssetLibrary->SetModelDirectoryName( modelDirectory ); pAssetLibrary->SetMaterialDirectoryName( materialDirectory ); pAssetLibrary->SetTextureDirectoryName( textureDirectory ); pAssetLibrary->SetShaderDirectoryName( shaderDirectory ); pAssetLibrary->SetFontDirectoryName( fontDirectory ); for(UINT ii=0; iiGetValueByName(materialValueName)->ValueAsString(); // Get/load material for this mesh cString meshSuffix = itoc(ii); CPUTMaterialDX11 *pMaterial = (CPUTMaterialDX11*)pAssetLibrary->GetMaterial(materialName, false, modelSuffix, meshSuffix); ASSERT( pMaterial, _L("Couldn't find material.") ); // set the material on this mesh // TODO: Model owns the materials. That allows different models to share meshes (aka instancing) that have different materials SetMaterial(ii, pMaterial); // Release the extra refcount we're holding from the GetMaterial operation earlier // now the asset library, and this model have the only refcounts on that material pMaterial->Release(); // Create two ID3D11InputLayout objects, one for each material. mpMesh[ii]->BindVertexShaderLayout( mpMaterial[ii], mpShadowCastMaterial); // mpShadowCastMaterial->Release() } return result; } // Set the material associated with this mesh and create/re-use a //----------------------------------------------------------------------------- void CPUTModelDX11::SetMaterial(UINT ii, CPUTMaterial *pMaterial) { CPUTModel::SetMaterial(ii, pMaterial); // Can't bind the layout if we haven't loaded the mesh yet. CPUTMeshDX11 *pMesh = (CPUTMeshDX11*)mpMesh[ii]; D3D11_INPUT_ELEMENT_DESC *pDesc = pMesh->GetLayoutDescription(); if( pDesc ) { pMesh->BindVertexShaderLayout(pMaterial, mpMaterial[ii]); } } ================================================ FILE: CPUT/CPUT/CPUTModelDX11.h ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #ifndef __CPUTMODELDX11_H__ #define __CPUTMODELDX11_H__ #include "CPUTModel.h" #include "CPUT_DX11.h" class CPUTMeshDX11; class CPUTRenderParametersDX; class CPUTMaterialDX11; //-------------------------------------------------------------------------------------- struct CPUTModelConstantBuffer { XMMATRIX World; XMMATRIX WorldViewProjection; XMMATRIX InverseWorld; XMVECTOR LightDirection; XMVECTOR EyePosition; XMMATRIX LightWorldViewProjection; XMMATRIX ViewProjection; XMVECTOR BoundingBoxCenterWorldSpace; XMVECTOR BoundingBoxHalfWorldSpace; XMVECTOR BoundingBoxCenterObjectSpace; XMVECTOR BoundingBoxHalfObjectSpace; }; //-------------------------------------------------------------------------------------- class CPUTModelDX11 : public CPUTModel { protected: ID3D11Buffer *mpModelConstantBuffer; // Destructor is not public. Must release instead of delete. ~CPUTModelDX11(){ SAFE_RELEASE(mpModelConstantBuffer); } public: CPUTModelDX11() : mpModelConstantBuffer(NULL) {} CPUTMeshDX11 *GetMesh(const UINT index) const; CPUTResult LoadModel(CPUTConfigBlock *pBlock, int *pParentID, CPUTModel *pMasterModel=NULL); void SetRenderStates(CPUTRenderParameters &renderParams); void Render(CPUTRenderParameters &renderParams); void RenderShadow(CPUTRenderParameters &renderParams); void SetMaterial(UINT ii, CPUTMaterial *pMaterial); void DrawBoundingBox(CPUTRenderParameters &renderParams); }; #endif // __CPUTMODELDX11_H__ ================================================ FILE: CPUT/CPUT/CPUTNullNode.cpp ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #include "CPUTNullNode.h" #include "CPUTOSServicesWin.h" // FOR TCHAR #include "CPUTConfigBlock.h" #include "CPUTAssetLibrary.h" // Parse the information in the .set file for this type of node //----------------------------------------------------------------------------- CPUTResult CPUTNullNode::LoadNullNode(CPUTConfigBlock *pBlock, int *pParentID) { CPUTResult result = CPUT_SUCCESS; // set the null/group node name mName = pBlock->GetValueByName(_L("name"))->ValueAsString(); // get the parent ID *pParentID = pBlock->GetValueByName(_L("parent"))->ValueAsInt(); LoadParentMatrixFromParameterBlock( pBlock ); return result; } ================================================ FILE: CPUT/CPUT/CPUTNullNode.h ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #ifndef __CPUTNULLNODE_H__ #define __CPUTNULLNODE_H__ #include "CPUTRenderNode.h" class CPUTConfigBlock; class CPUTNullNode:public CPUTRenderNode { public: CPUTNullNode(){} ~CPUTNullNode(){} CPUTResult LoadNullNode(CPUTConfigBlock *pBlock, int *pParentID); }; #endif // #ifndef __CPUTNULLNODE_H__ ================================================ FILE: CPUT/CPUT/CPUTOSServicesWin.cpp ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #include "CPUTOSServicesWin.h" #include "tchar.h" CPUTOSServices* CPUTOSServices::mpOSServices = NULL; // Constructor //----------------------------------------------------------------------------- CPUTOSServices::CPUTOSServices():mhWnd(NULL) { //mCPUTMediaDirectory.clear(); mCPUTResourceDirectory.clear(); } // Destructor //----------------------------------------------------------------------------- CPUTOSServices::~CPUTOSServices() { //mCPUTMediaDirectory.clear(); mCPUTResourceDirectory.clear(); } // Singleton GetOSServices() //----------------------------------------------------------------------------- CPUTOSServices* CPUTOSServices::GetOSServices() { if(NULL==mpOSServices) mpOSServices = new CPUTOSServices(); return mpOSServices; } // Singleton destroyer //----------------------------------------------------------------------------- CPUTResult CPUTOSServices::DeleteOSServices() { if(mpOSServices) { delete mpOSServices; mpOSServices = NULL; } return CPUT_SUCCESS; } // Get the OS window dimensions //----------------------------------------------------------------------------- void CPUTOSServices::GetClientDimensions(int *pX, int *pY, int *pWidth, int *pHeight) { RECT windowRect; if(0==GetClientRect(mhWnd, &windowRect)) { return; } *pX = windowRect.left; *pY = windowRect.top; *pWidth = windowRect.right - windowRect.left; *pHeight = windowRect.bottom - windowRect.top; } // Get the OS window client area dimensions //----------------------------------------------------------------------------- void CPUTOSServices::GetClientDimensions(int *pWidth, int *pHeight) { RECT windowRect; if(0==GetClientRect(mhWnd, &windowRect)) { return; } *pWidth = windowRect.right - windowRect.left; *pHeight = windowRect.bottom - windowRect.top; } // Get the desktop dimensions //----------------------------------------------------------------------------- void CPUTOSServices::GetDesktopDimensions(int *pWidth, int *pHeight) { *pWidth = GetSystemMetrics(SM_CXFULLSCREEN); // alternate method: GetSystemMetrics(SM_CXSCREEN); *pHeight = GetSystemMetrics(SM_CYFULLSCREEN); // alternate method: GetSystemMetrics(SM_CYSCREEN); } // Returns true if the window is currently maximized //----------------------------------------------------------------------------- bool CPUTOSServices::IsWindowMaximized() { WINDOWPLACEMENT WindowPlacement; WindowPlacement.length = sizeof(WindowPlacement); GetWindowPlacement(mhWnd, &WindowPlacement); if(SW_SHOWMAXIMIZED == WindowPlacement.showCmd) { return true; } return false; } // Returns true if the window is currently minimized //----------------------------------------------------------------------------- bool CPUTOSServices::IsWindowMinimized() { WINDOWPLACEMENT WindowPlacement; WindowPlacement.length = sizeof(WindowPlacement); GetWindowPlacement(mhWnd, &WindowPlacement); if(SW_SHOWMAXIMIZED == WindowPlacement.showCmd) { return true; } return false; } // Returns true if the CPUT window is currently the 'focused' window on the // desktop //----------------------------------------------------------------------------- bool CPUTOSServices::DoesWindowHaveFocus() { HWND hFocusedWindow = GetActiveWindow(); if(mhWnd == hFocusedWindow) { return true; } return false; } // Retrieves the current working directory //----------------------------------------------------------------------------- CPUTResult CPUTOSServices::GetWorkingDirectory(cString *pPath) { TCHAR pPathAsTchar[CPUT_MAX_PATH]; DWORD result = GetCurrentDirectory(CPUT_MAX_PATH, pPathAsTchar); ASSERT( result, _L("GetCurrentDirectory returned 0.") ); *pPath = pPathAsTchar; return CPUT_SUCCESS; } // Sets the current working directory //----------------------------------------------------------------------------- CPUTResult CPUTOSServices::SetWorkingDirectory(const cString &path) { BOOL result = SetCurrentDirectory(path.c_str()); ASSERT( 0 != result, _L("Error setting current directory.") ); return CPUT_SUCCESS; } // Gets the location of the executable's directory //----------------------------------------------------------------------------- CPUTResult CPUTOSServices::GetExecutableDirectory(cString *pExecutableDir) { TCHAR pFilename[CPUT_MAX_PATH]; DWORD result = GetModuleFileName(NULL, pFilename, CPUT_MAX_PATH); ASSERT( 0 != result, _L("Unable to get executable's working directory.")); // strip off the executable name+ext cString ResolvedPathAndFilename; ResolveAbsolutePathAndFilename(pFilename, &ResolvedPathAndFilename); cString Drive, Dir, Filename, Ext; SplitPathAndFilename(ResolvedPathAndFilename, &Drive, &Dir, &Filename, &Ext); // store and return *pExecutableDir = Drive + Dir; return CPUT_SUCCESS; } // Split up the supplied path+fileName into its constituent parts //----------------------------------------------------------------------------- CPUTResult CPUTOSServices::SplitPathAndFilename(const cString &sourceFilename, cString *pDrive, cString *pDir, cString *pFileName, cString *pExtension) { TCHAR pSplitDrive[CPUT_MAX_PATH]; TCHAR pSplitDirs[CPUT_MAX_PATH]; TCHAR pSplitFile[CPUT_MAX_PATH]; TCHAR pSplitExt[CPUT_MAX_PATH]; #if defined (UNICODE) || defined(_UNICODE) #define SPLITPATH _wsplitpath_s #else #define SPLITPATH _splitpath_s #endif errno_t result = SPLITPATH(sourceFilename.c_str(), pSplitDrive, CPUT_MAX_PATH, pSplitDirs, CPUT_MAX_PATH, pSplitFile, CPUT_MAX_PATH, pSplitExt, CPUT_MAX_PATH); ASSERT( 0 == result, _L("Error splitting path") ); // return the items the user wants *pDrive = pSplitDrive; *pDir = pSplitDirs; *pFileName = pSplitFile; *pExtension = pSplitExt; return CPUT_SUCCESS; } // Takes a relative/full path+fileName and returns the absolute path with drive // letter, absolute path, fileName and extension of this file. // Truncates total path/file length to CPUT_MAX_PATH //----------------------------------------------------------------------------- CPUTResult CPUTOSServices::ResolveAbsolutePathAndFilename(const cString &fileName, cString *pResolvedPathAndFilename) { TCHAR pFullPathAndFilename[CPUT_MAX_PATH]; DWORD result = GetFullPathName(fileName.c_str(), CPUT_MAX_PATH, pFullPathAndFilename, NULL); ASSERT( 0 != result, _L("Error getting full path name") ); *pResolvedPathAndFilename = pFullPathAndFilename; return CPUT_SUCCESS; } // Verifies that file exists at specified path //----------------------------------------------------------------------------- CPUTResult CPUTOSServices::DoesFileExist(const cString &pathAndFilename) { // check for file existence // attempt to open it where they said it was FILE *pFile = NULL; #if defined (UNICODE) || defined(_UNICODE) errno_t err = _wfopen_s(&pFile, pathAndFilename.c_str(), _L("r")); #else errno_t err = fopen_s(&pFile, pathAndFilename.c_str(), _L("r")); #endif if(0 == err) { // yep - file exists fclose(pFile); return CPUT_SUCCESS; } // not found, translate the file error and return it return TranslateFileError(err); } // Verifies that directory exists. // Returns success if the directory exists and is readable (failure may mean // it's busy or permissions denied on win32) //----------------------------------------------------------------------------- CPUTResult CPUTOSServices::DoesDirectoryExist(const cString &path) { DWORD fileAttribs; #if defined (UNICODE) || defined(_UNICODE) fileAttribs = GetFileAttributesW(path.c_str()); #else fileAttribs = GetFileAttributesA(path.c_str()); #endif ASSERT( INVALID_FILE_ATTRIBUTES != fileAttribs, _L("Failed getting file attributes") ); return CPUT_SUCCESS; } // Open a file and return file pointer to it //----------------------------------------------------------------------------- CPUTResult CPUTOSServices::OpenFile(const cString &fileName, FILE **ppFilePointer) { #if defined (UNICODE) || defined(_UNICODE) errno_t err = _wfopen_s(ppFilePointer, fileName.c_str(), _L("r")); #else errno_t err = fopen_s(ppFilePointer, fileName.c_str(), "r"); #endif return TranslateFileError(err); } // Read the entire contents of a file and return a pointer/size to it //----------------------------------------------------------------------------- CPUTResult CPUTOSServices::ReadFileContents(const cString &fileName, UINT *pSizeInBytes, void **ppData) { FILE *pFile = NULL; #if defined (UNICODE) || defined(_UNICODE) errno_t err = _wfopen_s(&pFile, fileName.c_str(), _L("r")); #else errno_t err = fopen_s(&pFile, fileName.c_str(), "r"); #endif if(0 == err) { // get file size fseek(pFile, 0, SEEK_END); *pSizeInBytes = ftell(pFile); fseek (pFile, 0, SEEK_SET); // allocate buffer *ppData = (void*) new char[*pSizeInBytes]; ASSERT( ppData, _L("Out of memory") ); // read it all in UINT numBytesRead = (UINT) fread(*ppData, sizeof(char), *pSizeInBytes, pFile); ASSERT( numBytesRead == *pSizeInBytes, _L("File read byte count mismatch.") ); // close and return fclose(pFile); return CPUT_SUCCESS; } // some kind of file error, translate the error code and return it return TranslateFileError(err); } // Open the OS's 'open a file' dialog box //----------------------------------------------------------------------------- CPUTResult CPUTOSServices::OpenFileDialog(const cString &filter, cString *pfileName) { OPENFILENAME ofn; // common dialog box structure TCHAR szFile[260]; // buffer for file name // Initialize OPENFILENAME UNREFERENCED_PARAMETER(filter); ZeroMemory(&ofn, sizeof(ofn)); ofn.lStructSize = sizeof(ofn); ofn.hwndOwner = mhWnd; ofn.lpstrFilter = _L("All\0*.*\0Text\0*.TXT\0\0");//Filter.c_str(); //"All\0*.*\0Text\0*.TXT\0"; ofn.lpstrFile = szFile; // Set lpstrFile[0] to '\0' so that GetOpenFileName does not // use the contents of szFile to initialize itself. ofn.lpstrFile[0] = '\0'; ofn.nMaxFile = sizeof(szFile); ofn.nFilterIndex = 1; ofn.lpstrFileTitle = NULL; ofn.nMaxFileTitle = 0; ofn.lpstrInitialDir = NULL; ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST; if(0==GetOpenFileName(&ofn)) { return CPUT_WARNING_CANCELED; } *pfileName = szFile; return CPUT_SUCCESS; } // Open a system dialog box //----------------------------------------------------------------------------- CPUTResult CPUTOSServices::OpenMessageBox(cString title, cString text) { ::MessageBox(NULL, text.c_str(), title.c_str(), MB_OK); return CPUT_SUCCESS; } // Returns whether the specified control key is pressed or not //----------------------------------------------------------------------------- bool CPUTOSServices::ControlKeyPressed(CPUTKey &key) { int nVirtKey; switch(key) { case KEY_LEFT_SHIFT: nVirtKey = VK_LSHIFT; break; case KEY_RIGHT_SHIFT: nVirtKey = VK_RSHIFT; break; case KEY_LEFT_CTRL: nVirtKey = VK_LCONTROL; break; case KEY_RIGHT_CTRL: nVirtKey = VK_RCONTROL; break; case KEY_LEFT_ALT: nVirtKey = VK_LMENU; break; case KEY_RIGHT_ALT: nVirtKey = VK_RMENU; break; default: return false; }; SHORT result = GetKeyState(nVirtKey); // return true/false if handled return (result & 0x8000)!=0; } // mouse // this function 'captures' the mouse and makes it ONLY available to this app // User cannot click on any other app until you call ReleaseMouse, so use this // carefully //----------------------------------------------------------------------------- void CPUTOSServices::CaptureMouse() { SetCapture(mhWnd); } // Releases a captured mouse //----------------------------------------------------------------------------- void CPUTOSServices::ReleaseMouse() { ReleaseCapture(); } // outputs a string to the debug out. // In Visual Studio - this is the Output pane //----------------------------------------------------------------------------- void CPUTOSServices::OutputConsoleString(cString &OutputString) { OutputDebugString(OutputString.c_str()); } // Did file open error indicate a returnable file/system problem? (or just not found) //----------------------------------------------------------------------------- bool CPUTOSServices::FileFoundButWithError(CPUTResult result) { bool IsError = false; // Is the result a file error code? switch(result) { case CPUT_ERROR_FILE_IO_ERROR: case CPUT_ERROR_FILE_NOT_ENOUGH_MEMORY: case CPUT_ERROR_FILE_PERMISSION_DENIED: case CPUT_ERROR_FILE_DEVICE_OR_RESOURCE_BUSY: case CPUT_ERROR_FILE_IS_A_DIRECTORY: case CPUT_ERROR_FILE_TOO_MANY_OPEN_FILES: case CPUT_ERROR_FILE_TOO_LARGE: case CPUT_ERROR_FILE_FILENAME_TOO_LONG: IsError = true; break; default: // nope - good to go IsError = false; } return IsError; } // Translate a file operation error code //----------------------------------------------------------------------------- CPUTResult CPUTOSServices::TranslateFileError(errno_t err) { if(0==err) { return CPUT_SUCCESS; } // see: http://msdn.microsoft.com/en-us/library/t3ayayh1.aspx // for list of all error codes CPUTResult result = CPUT_ERROR_FILE_ERROR; switch(err) { case ENOENT: result = CPUT_ERROR_FILE_NOT_FOUND; break; // file/dir not found case EIO: result = CPUT_ERROR_FILE_IO_ERROR; break; case ENXIO: result = CPUT_ERROR_FILE_NO_SUCH_DEVICE_OR_ADDRESS; break; case EBADF: result = CPUT_ERROR_FILE_BAD_FILE_NUMBER; break; case ENOMEM: result = CPUT_ERROR_FILE_NOT_ENOUGH_MEMORY; break; case EACCES: result = CPUT_ERROR_FILE_PERMISSION_DENIED; break; case EBUSY: result = CPUT_ERROR_FILE_DEVICE_OR_RESOURCE_BUSY; break; case EEXIST: result = CPUT_ERROR_FILE_EXISTS; break; case EISDIR: result = CPUT_ERROR_FILE_IS_A_DIRECTORY; break; case ENFILE: result = CPUT_ERROR_FILE_TOO_MANY_OPEN_FILES; break; case EFBIG: result = CPUT_ERROR_FILE_TOO_LARGE; break; case ENOSPC: result = CPUT_ERROR_FILE_DEVICE_FULL; break; case ENAMETOOLONG: result = CPUT_ERROR_FILE_FILENAME_TOO_LONG; break; default: // unknown file error type - assert so you can add it to the list ASSERT(0,_L("Unkown error code")); } return result; } #ifdef CPUT_GPA_INSTRUMENTATION // Allows you to get the global/domain-wide instrumentation markers needed // to mark events in GPA //----------------------------------------------------------------------------- void CPUTOSServices::GetInstrumentationPointers(__itt_domain **ppGPADomain, CPUT_GPA_INSTRUMENTATION_STRINGS eString, __itt_string_handle **ppGPAStringHandle) { *ppGPADomain = mpGPADomain; *ppGPAStringHandle = mppGPAStringHandles[eString]; } // Set the global/domain-wide instrumtation markers needed to mark events // in GPA //----------------------------------------------------------------------------- void CPUTOSServices::SetInstrumentationPointers(__itt_domain *pGPADomain, CPUT_GPA_INSTRUMENTATION_STRINGS eString, __itt_string_handle *pGPAStringHandle) { mpGPADomain = pGPADomain; mppGPAStringHandles[eString] = pGPAStringHandle; } #endif ================================================ FILE: CPUT/CPUT/CPUTOSServicesWin.h ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #ifndef __CPUTOSServicesWin_H__ #define __CPUTOSServicesWin_H__ #include "CPUT.h" // OS includes #include #include // file open error codes #include // wstring class CPUTOSServices { public: static CPUTOSServices* GetOSServices(); static CPUTResult DeleteOSServices(); // screen/window dimensions void GetClientDimensions( int *pWidth, int *pHeight); void GetClientDimensions( int *pX, int *pY, int *pWidth, int *pHeight); void GetDesktopDimensions(int *pWidth, int *pHeight); bool IsWindowMaximized(); bool IsWindowMinimized(); bool DoesWindowHaveFocus(); // Mouse capture - 'binds'/releases all mouse input to this window void CaptureMouse(); void ReleaseMouse(); //Working directory manipulation CPUTResult GetWorkingDirectory(cString *pPath); CPUTResult SetWorkingDirectory(const cString &path); CPUTResult GetExecutableDirectory(cString *pExecutableDir); // Path helpers CPUTResult ResolveAbsolutePathAndFilename(const cString &fileName, cString *pResolvedPathAndFilename); CPUTResult SplitPathAndFilename(const cString &sourceFilename, cString *pDrive, cString *pDir, cString *pfileName, cString *pExtension); // file handling CPUTResult DoesFileExist(const cString &pathAndFilename); CPUTResult DoesDirectoryExist(const cString &path); CPUTResult OpenFile(const cString &fileName, FILE **pFilePointer); CPUTResult ReadFileContents(const cString &fileName, UINT *psizeInBytes, void **ppData); // File dialog box CPUTResult OpenFileDialog(const cString &filter, cString *pfileName); // Informational Message box CPUTResult OpenMessageBox(cString title, cString text); // error handling inline void Assert(bool bCondition) {assert(bCondition);} void OutputConsoleString(cString &outputString); CPUTResult TranslateFileError(errno_t err); // hwnd setup inline void SethWnd(const HWND hWnd) { mhWnd = hWnd; }; inline void GetWindowHandle(HWND *phWnd) { *phWnd = mhWnd; }; // special keys bool ControlKeyPressed(CPUTKey &key); private: CPUTOSServices(); ~CPUTOSServices(); static CPUTOSServices *mpOSServices; // singleton object HWND mhWnd; cString mCPUTResourceDirectory; bool FileFoundButWithError(CPUTResult result); #ifdef CPUT_GPA_INSTRUMENTATION public: // GPA instrumentation (only available in Profile build) void GetInstrumentationPointers(__itt_domain **ppGPADomain, CPUT_GPA_INSTRUMENTATION_STRINGS eString, __itt_string_handle **ppGPAStringHandle); void SetInstrumentationPointers(__itt_domain *pGPADomain, CPUT_GPA_INSTRUMENTATION_STRINGS eString, __itt_string_handle *pGPAStringHandle); private: // GPA instrumentation member variables __itt_domain *mpGPADomain; __itt_string_handle *mppGPAStringHandles[GPA_HANDLE_STRING_ENUMS_SIZE]; #endif }; #endif // __CPUTOSServicesWin_H__ ================================================ FILE: CPUT/CPUT/CPUTPerfTaskMarker.cpp ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #include "CPUTPerfTaskMarker.h" #ifdef CPUT_GPA_INSTRUMENTATION // constructor // automatically creates an ITT begin marker at the start of this task //----------------------------------------------------------------------------- CPUTPerfTaskMarker::CPUTPerfTaskMarker(DWORD color, wchar_t *pString) { D3DPERF_BeginEvent(color, pString); } // destructor // when class goes out of scope, this marker will automatically be called //----------------------------------------------------------------------------- CPUTPerfTaskMarker::~CPUTPerfTaskMarker() { D3DPERF_EndEvent(); } #else // This is a bit of a hack to get the compiler not to complain about this being an empty file // during the compilation in any mode that doesn't have CPUT_GPA_INSTRUMENTATION defined #define CPUTPerfTaskMarkerNotEmpty() namespace { char CPUTPerfTaskMarkerDummy##__LINE__; } CPUTPerfTaskMarkerNotEmpty(); #endif ================================================ FILE: CPUT/CPUT/CPUTPerfTaskMarker.h ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #ifndef __CPUTPERFTASKMARKER_H__ #define __CPUTPERFTASKMARKER_H__ #include "CPUT.h" #ifdef CPUT_GPA_INSTRUMENTATION // GPA instrumentation helper class - only available in profile build // Allows you to easily add 'task markers' to certain events //----------------------------------------------------------------------------- class CPUTPerfTaskMarker { public: CPUTPerfTaskMarker(DWORD color, wchar_t *pString); ~CPUTPerfTaskMarker(); private: }; #endif #endif // #ifndef __CPUTPERFTASKMARKER_H__ ================================================ FILE: CPUT/CPUT/CPUTPixelShaderDX11.cpp ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #include "CPUTPixelShaderDX11.h" #include "CPUTAssetLibraryDX11.h" #include "D3DCompiler.h" CPUTPixelShaderDX11 *CPUTPixelShaderDX11::CreatePixelShader( const cString &name, ID3D11Device *pD3dDevice, const cString &shaderMain, const cString &shaderProfile ) { ID3DBlob *pCompiledBlob = NULL; ID3D11PixelShader *pNewPixelShader = NULL; CPUTAssetLibraryDX11 *pAssetLibrary = (CPUTAssetLibraryDX11*)CPUTAssetLibrary::GetAssetLibrary(); CPUTResult result = pAssetLibrary->CompileShaderFromFile(name, shaderMain, shaderProfile, &pCompiledBlob); ASSERT( CPUTSUCCESS(result), _L("Error compiling pixel shader:\n\n") ); // Create the pixel shader // TODO: Move to pixel shader class HRESULT hr = pD3dDevice->CreatePixelShader( pCompiledBlob->GetBufferPointer(), pCompiledBlob->GetBufferSize(), NULL, &pNewPixelShader ); ASSERT( SUCCEEDED(hr), _L("Error creating pixel shader:\n\n") ); // cString DebugName = _L("CPUTAssetLibraryDX11::GetPixelShader ")+name; // CPUTSetDebugName(pNewPixelShader, DebugName); CPUTPixelShaderDX11 *pNewCPUTPixelShader = new CPUTPixelShaderDX11( pNewPixelShader, pCompiledBlob ); // add shader to library pAssetLibrary->AddPixelShader(name + shaderMain + shaderProfile, pNewCPUTPixelShader); // pNewCPUTPixelShader->Release(); // We've added it to the library, so release our reference // return the shader (and blob) return pNewCPUTPixelShader; } //-------------------------------------------------------------------------------------- CPUTPixelShaderDX11 *CPUTPixelShaderDX11::CreatePixelShaderFromMemory( const cString &name, ID3D11Device *pD3dDevice, const cString &shaderMain, const cString &shaderProfile, const char *pShaderSource ) { ID3DBlob *pCompiledBlob = NULL; ID3D11PixelShader *pNewPixelShader = NULL; CPUTAssetLibraryDX11 *pAssetLibrary = (CPUTAssetLibraryDX11*)CPUTAssetLibrary::GetAssetLibrary(); CPUTResult result = pAssetLibrary->CompileShaderFromMemory(pShaderSource, shaderMain, shaderProfile, &pCompiledBlob); ASSERT( CPUTSUCCESS(result), _L("Error compiling pixel shader:\n\n") ); // Create the pixel shader // TODO: Move to pixel shader class HRESULT hr = pD3dDevice->CreatePixelShader( pCompiledBlob->GetBufferPointer(), pCompiledBlob->GetBufferSize(), NULL, &pNewPixelShader ); ASSERT( SUCCEEDED(hr), _L("Error creating pixel shader:\n\n") ); // cString DebugName = _L("CPUTAssetLibraryDX11::GetPixelShader ")+name; // CPUTSetDebugName(pNewPixelShader, DebugName); CPUTPixelShaderDX11 *pNewCPUTPixelShader = new CPUTPixelShaderDX11( pNewPixelShader, pCompiledBlob ); // add shader to library pAssetLibrary->AddPixelShader(name + shaderMain + shaderProfile, pNewCPUTPixelShader); // pNewCPUTPixelShader->Release(); // We've added it to the library, so release our reference // return the shader (and blob) return pNewCPUTPixelShader; } ================================================ FILE: CPUT/CPUT/CPUTPixelShaderDX11.h ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #ifndef _CPUTPIXELSHADERDX11_H #define _CPUTPIXELSHADERDX11_H #include "CPUT.h" #include "CPUTShaderDX11.h" class CPUTPixelShaderDX11 : public CPUTShaderDX11 { protected: ID3D11PixelShader *mpPixelShader; // Destructor is not public. Must release instead of delete. ~CPUTPixelShaderDX11(){ SAFE_RELEASE(mpPixelShader); } public: static CPUTPixelShaderDX11 *CreatePixelShader( const cString &name, ID3D11Device *pD3dDevice, const cString &shaderMain, const cString &shaderProfile ); static CPUTPixelShaderDX11 *CreatePixelShaderFromMemory( const cString &name, ID3D11Device *pD3dDevice, const cString &shaderMain, const cString &shaderProfile, const char *pShaderSource ); CPUTPixelShaderDX11() : mpPixelShader(NULL), CPUTShaderDX11(NULL) {} CPUTPixelShaderDX11(ID3D11PixelShader *pD3D11PixelShader, ID3DBlob *pBlob) : mpPixelShader(pD3D11PixelShader), CPUTShaderDX11(pBlob) {} ID3D11PixelShader *GetNativePixelShader() { return mpPixelShader; } }; #endif //_CPUTPIXELSHADER_H ================================================ FILE: CPUT/CPUT/CPUTPostProcess.cpp ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #include "CPUT_DX11.h" #include "CPUTPostProcess.h" #include "CPUTRenderTarget.h" #include "CPUTAssetLibrary.h" #include "CPUTMaterial.h" #include "CPUTSprite.h" //----------------------------------------- CPUTPostProcess::~CPUTPostProcess() { SAFE_DELETE( mpFullScreenSprite ); SAFE_RELEASE( mpMaterialComposite ); SAFE_RELEASE( mpMaterialBlurVertical ); SAFE_RELEASE( mpMaterialBlurHorizontal ); SAFE_RELEASE( mpMaterialDownSampleLogLum ); SAFE_RELEASE( mpMaterialDownSample4x4Alpha ); SAFE_RELEASE( mpMaterialDownSample4x4 ); SAFE_RELEASE( mpMaterialDownSampleBackBuffer4x4 ); SAFE_RELEASE( mpMaterialSpriteNoAlpha ); SAFE_DELETE(mpRT1x1 ); SAFE_DELETE(mpRT4x4 ); SAFE_DELETE(mpRT64x64 ); SAFE_DELETE(mpRTDownSample4x4PingPong ); SAFE_DELETE(mpRTDownSample4x4 ); // SAFE_DELETE(mpRTSourceRenderTarget ); // We don't allocate this. Don't delete it. } //----------------------------------------- void CPUTPostProcess::CreatePostProcess( CPUTRenderTargetColor *pSourceRenderTarget ){ mpRTSourceRenderTarget = pSourceRenderTarget; DXGI_FORMAT sourceFormat = mpRTSourceRenderTarget->GetColorFormat(); UINT sourceWidth = mpRTSourceRenderTarget->GetWidth(); UINT sourceHeight = mpRTSourceRenderTarget->GetHeight(); mpRTDownSample4x4 = new CPUTRenderTargetColor(); mpRTDownSample4x4PingPong = new CPUTRenderTargetColor(); mpRT64x64 = new CPUTRenderTargetColor(); mpRT4x4 = new CPUTRenderTargetColor(); mpRT1x1 = new CPUTRenderTargetColor(); mpRTDownSample4x4->CreateRenderTarget( _L("$PostProcessDownsample4x4"), sourceWidth/4, sourceHeight/4, sourceFormat ); mpRTDownSample4x4PingPong->CreateRenderTarget( _L("$PostProcessDownsample4x4PingPong"), sourceWidth/4, sourceHeight/4, sourceFormat ); mpRT64x64->CreateRenderTarget( _L("$PostProcessRT64x64"), 64, 64, DXGI_FORMAT_R32_FLOAT ); mpRT4x4->CreateRenderTarget( _L("$PostProcessRT4x4"), 8, 8, DXGI_FORMAT_R32_FLOAT ); mpRT1x1->CreateRenderTarget( _L("$PostProcessRT1x1"), 1, 1, DXGI_FORMAT_R32_FLOAT ); CPUTAssetLibrary *pLibrary = CPUTAssetLibrary::GetAssetLibrary(); mpMaterialDownSampleBackBuffer4x4 = pLibrary->GetMaterial(_L("PostProcess/DownSampleBackBuffer4x4")); mpMaterialDownSample4x4 = pLibrary->GetMaterial(_L("PostProcess/DownSample4x4")); mpMaterialDownSample4x4Alpha = pLibrary->GetMaterial(_L("PostProcess/DownSample4x4Alpha")); mpMaterialDownSampleLogLum = pLibrary->GetMaterial(_L("PostProcess/DownSampleLogLum")); mpMaterialBlurHorizontal = pLibrary->GetMaterial(_L("PostProcess/BlurHorizontal")); mpMaterialBlurVertical = pLibrary->GetMaterial(_L("PostProcess/BlurVertical")); mpMaterialComposite = pLibrary->GetMaterial(_L("PostProcess/Composite")); mpMaterialSpriteNoAlpha = pLibrary->GetMaterial(_L("PostProcess/Sprite")); mpFullScreenSprite = new CPUTSprite(); mpFullScreenSprite->CreateSprite( -1.0f, -1.0f, 2.0f, 2.0f, _L("Sprite") ); } UINT gPostProcessingMode = 0; //----------------------------------------- void CPUTPostProcess::PerformPostProcess( CPUTRenderParameters &renderParams ) { mpRTDownSample4x4->SetRenderTarget( renderParams); mpFullScreenSprite->DrawSprite( renderParams, *mpMaterialDownSampleBackBuffer4x4 ); mpRTDownSample4x4->RestoreRenderTarget( renderParams ); // Compute average of log of luminance by downsampling log to 64x64, then 4x4, then 1x1 mpRT64x64->SetRenderTarget(renderParams); mpFullScreenSprite->DrawSprite(renderParams, *mpMaterialDownSampleLogLum); mpRT64x64->RestoreRenderTarget( renderParams ); mpRT4x4->SetRenderTarget(renderParams); mpFullScreenSprite->DrawSprite(renderParams, *mpMaterialDownSample4x4); mpRT4x4->RestoreRenderTarget( renderParams ); mpRT1x1->SetRenderTarget(renderParams); mpFullScreenSprite->DrawSprite( renderParams, *mpMaterialDownSample4x4Alpha ); // Partially blend with previous to smooth result over time mpRT1x1->RestoreRenderTarget( renderParams ); // Better blur for bloom UINT ii; UINT numBlurs = 1; // TODO: expose as a config param for( ii=0; iiSetRenderTarget(renderParams); mpFullScreenSprite->DrawSprite( renderParams, *mpMaterialBlurHorizontal ); mpRTDownSample4x4PingPong->RestoreRenderTarget( renderParams ); mpRTDownSample4x4->SetRenderTarget( renderParams); mpFullScreenSprite->DrawSprite( renderParams, *mpMaterialBlurVertical ); mpRTDownSample4x4->RestoreRenderTarget( renderParams ); } mpFullScreenSprite->DrawSprite(renderParams, *mpMaterialComposite); } ================================================ FILE: CPUT/CPUT/CPUTPostProcess.h ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #ifndef _CPUTPOSTPROCESS_H #define _CPUTPOSTPROCESS_H class CPUTRenderTargetColor; class CPUTMaterial; class CPUTRenderParameters; class CPUTSprite; class CPUTPostProcess { protected: CPUTRenderTargetColor *mpRTSourceRenderTarget; CPUTRenderTargetColor *mpRTDownSample4x4; CPUTRenderTargetColor *mpRTDownSample4x4PingPong; CPUTRenderTargetColor *mpRT64x64; CPUTRenderTargetColor *mpRT4x4; CPUTRenderTargetColor *mpRT1x1; CPUTMaterial *mpMaterialSpriteNoAlpha; CPUTMaterial *mpMaterialDownSampleBackBuffer4x4; CPUTMaterial *mpMaterialDownSample4x4; CPUTMaterial *mpMaterialDownSample4x4Alpha; CPUTMaterial *mpMaterialDownSampleLogLum; CPUTMaterial *mpMaterialBlurHorizontal; CPUTMaterial *mpMaterialBlurVertical; CPUTMaterial *mpMaterialComposite; CPUTSprite *mpFullScreenSprite; public: CPUTPostProcess() : mpRTSourceRenderTarget(NULL), mpRTDownSample4x4(NULL), mpRTDownSample4x4PingPong(NULL), mpRT64x64(NULL), mpRT4x4(NULL), mpRT1x1(NULL), mpMaterialSpriteNoAlpha(NULL), mpMaterialDownSampleBackBuffer4x4(NULL), mpMaterialDownSample4x4(NULL), mpMaterialDownSample4x4Alpha(NULL), mpMaterialDownSampleLogLum(NULL), mpMaterialBlurHorizontal(NULL), mpMaterialBlurVertical(NULL), mpMaterialComposite(NULL), mpFullScreenSprite(NULL) {} ~CPUTPostProcess(); void CreatePostProcess( CPUTRenderTargetColor *pSourceRenderTarget ); void PerformPostProcess(CPUTRenderParameters &renderParams); }; #endif // _CPUTPOSTPROCESS_H ================================================ FILE: CPUT/CPUT/CPUTRefCount.h ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #ifndef __CPUTREFCOUNT_H__ #define __CPUTREFCOUNT_H__ #include "CPUT.h" // Reference counting class //----------------------------------------------------------------------------- class CPUTRefCount { private: mutable UINT mRefCount; protected: virtual ~CPUTRefCount(){} // Destructor is not public. Must release instead of delete. public: CPUTRefCount():mRefCount(1){} int AddRef() const { return ++mRefCount; } int GetRefCount() const { return mRefCount; } int Release() const { UINT u = --mRefCount; if(0==mRefCount) { delete this; } return u; } }; #endif // __CPUTREFCOUNT_H__ ================================================ FILE: CPUT/CPUT/CPUTRenderNode.cpp ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #include "CPUTRenderNode.h" #include "CPUTOSServicesWin.h" // for OutputDebugString(); // Constructor //----------------------------------------------------------------------------- CPUTRenderNode::CPUTRenderNode(): mpParent(NULL), mpChild(NULL), mpSibling(NULL) { // set transform to identity mWorldMatrix = float4x4Identity(); mParentMatrix = float4x4Identity(); // always create with everything dirty mWorldMatrixDirty = true; } // Destructor //----------------------------------------------------------------------------- CPUTRenderNode::~CPUTRenderNode() { SAFE_RELEASE(mpParent); SAFE_RELEASE(mpChild); SAFE_RELEASE(mpSibling); } //----------------------------------------------------------------------------- int CPUTRenderNode::ReleaseRecursive() { // #define OUTPUT_DEBUG_INFO #ifdef OUTPUT_DEBUG_INFO char pRefCountString[64]; sprintf_s( pRefCountString, 64, "(%d):", GetRefCount()-1 ); OutputDebugStringA( pRefCountString ); cString msg = GetName() + _L("\n"); OutputDebugString( msg.c_str() ); if( mpParent ) { OutputDebugString( _L("Parent: ") ); sprintf_s( pRefCountString, 64, "(%d):", mpParent->GetRefCount()-1 ); OutputDebugStringA( pRefCountString ); cString msg = (mpParent ? mpParent->GetName() : _L("NULL\n")) + _L("\n"); OutputDebugString( msg.c_str() ); } #endif // Release the parent. Note: we don't want to recursively release it, or it would release us = infinite loop. SAFE_RELEASE(mpParent); // Recursively release our children and siblings if( mpChild ) { #ifdef OUTPUT_DEBUG_INFO OutputDebugString( _L("Child") ); #endif if( !mpChild->ReleaseRecursive() ) { mpChild = NULL; } } if( mpSibling ) { #ifdef OUTPUT_DEBUG_INFO OutputDebugString( _L("Sibling:") ); #endif int refCount = mpSibling->ReleaseRecursive(); if( !refCount ) { mpSibling = NULL; } } return CPUTRefCount::Release(); } // parent/child/sibling //----------------------------------------------------------------------------- void CPUTRenderNode::SetParent(CPUTRenderNode *pParent) { SAFE_RELEASE(mpParent); if(NULL!=pParent) { pParent->AddRef(); } mpParent = pParent; } //----------------------------------------------------------------------------- void CPUTRenderNode::AddChild(CPUTRenderNode *pNode ) { ASSERT( NULL != pNode, _L("Can't add NULL node.") ); if( mpChild ) { mpChild->AddSibling( pNode ); } else { pNode->AddRef(); mpChild = pNode; } } //----------------------------------------------------------------------------- void CPUTRenderNode::AddSibling(CPUTRenderNode *pNode ) { ASSERT( NULL != pNode, _L("Can't add NULL node.") ); if( mpSibling ) { mpSibling->AddSibling( pNode ); } else { mpSibling = pNode; pNode->AddRef(); } } // Return the model's cumulative transform //----------------------------------------------------------------------------- float4x4* CPUTRenderNode::GetWorldMatrix() { if(mWorldMatrixDirty) { if(NULL!=mpParent) { float4x4 *pParentWorldMatrix = mpParent->GetWorldMatrix(); mWorldMatrix = mParentMatrix * *pParentWorldMatrix; } else { mWorldMatrix = mParentMatrix; } mWorldMatrixDirty = false; } // copy it return &mWorldMatrix; } // Recursively visit all sub-nodes in breadth-first mode and mark their // cumulative transforms as dirty //----------------------------------------------------------------------------- void CPUTRenderNode::MarkDirty() { mWorldMatrixDirty = true; if(mpSibling) { mpSibling->MarkDirty(); } if(mpChild) { mpChild->MarkDirty(); } } // Update - recursively visit all sub-nodes in breadth-first mode // Likely used for animation with a frame# or timestamp passed in // so that the update routine would calculate the new transforms // and called before Render() function //----------------------------------------------------------------------------- void CPUTRenderNode::UpdateRecursive( float deltaSeconds ) { // TODO: Need to Update this node first. Update(deltaSeconds); if(mpSibling) { mpSibling->UpdateRecursive(deltaSeconds); } if(mpChild) { mpChild->UpdateRecursive(deltaSeconds); } } // RenderRecursive - recursively visit all sub-nodes in breadth-first mode //----------------------------------------------------------------------------- void CPUTRenderNode::RenderRecursive(CPUTRenderParameters &renderParams) { Render(renderParams); if(mpChild) { mpChild->RenderRecursive(renderParams); CPUTRenderNode *pNode = mpChild->GetSibling(); while(pNode) { pNode->RenderRecursive(renderParams); pNode = pNode->GetSibling(); } } } // RenderRecursive - recursively visit all sub-nodes in breadth-first mode //----------------------------------------------------------------------------- void CPUTRenderNode::RenderShadowRecursive(CPUTRenderParameters &renderParams) { RenderShadow(renderParams); if(mpChild) { mpChild->RenderShadowRecursive(renderParams); CPUTRenderNode *pNode = mpChild->GetSibling(); while(pNode) { pNode->RenderShadowRecursive(renderParams); pNode = pNode->GetSibling(); } } } ================================================ FILE: CPUT/CPUT/CPUTRenderNode.h ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #ifndef __CPUTRENDERNODE_H__ #define __CPUTRENDERNODE_H__ #include "CPUTRenderParams.h" #include "CPUTRefCount.h" #include "CPUTMath.h" #include "CPUTConfigBlock.h" // forward declarations class CPUTCamera; class CPUTRenderNode : public CPUTRefCount { protected: cString mName; CPUTRenderNode *mpParent; CPUTRenderNode *mpChild; CPUTRenderNode *mpSibling; bool mWorldMatrixDirty; float4x4 mWorldMatrix; // transform of this object combined with it's parent(s) transform(s) float4x4 mParentMatrix; // transform of this object relative to it's parent cString mPrefix; ~CPUTRenderNode(); // Destructor is not public. Must release instead of delete. public: CPUTRenderNode(); void Scale(float xx, float yy, float zz) { float4x4 scale( xx, 0.0f, 0.0f, 0.0f, 0.0f, yy, 0.0f, 0.0f, 0.0f, 0.0f, zz, 0.0f, 0.0f, 0.0f, 0.0f, 1 ); mParentMatrix = mParentMatrix * scale; MarkDirty(); } void Scale(float xx) { float4x4 scale( xx, 0.0f, 0.0f, 0.0f, 0.0f, xx, 0.0f, 0.0f, 0.0f, 0.0f, xx, 0.0f, 0.0f, 0.0f, 0.0f, 1 ); mParentMatrix = mParentMatrix * scale; MarkDirty(); } void SetPosition(float x, float y, float z) { mParentMatrix.r3.x = x; mParentMatrix.r3.y = y; mParentMatrix.r3.z = z; MarkDirty(); } void SetPosition(float3 &position) { mParentMatrix.r3.x = position.x; mParentMatrix.r3.y = position.y; mParentMatrix.r3.z = position.z; MarkDirty(); } void GetPosition(float *pX, float *pY, float *pZ) { *pX = mParentMatrix.r3.x; *pY = mParentMatrix.r3.y; *pZ = mParentMatrix.r3.z; } void GetPosition(float3 *pPosition) { pPosition->x = mParentMatrix.r3.x; pPosition->y = mParentMatrix.r3.y; pPosition->z = mParentMatrix.r3.z; } float3 GetPosition() { float3 ret = float3( mParentMatrix.r3.x, mParentMatrix.r3.y, mParentMatrix.r3.z); return ret; } float3 GetLook() { return mParentMatrix.getZAxis(); } float3 GetUp() { return mParentMatrix.getYAxis(); } float3 GetLook( float *pX, float *pY, float *pZ ) { float3 look = mParentMatrix.getZAxis(); *pX = look.x; *pY = look.y; *pZ = look.z; } virtual int ReleaseRecursive(); void SetName(const cString &name) { mName = name;}; void SetParent(CPUTRenderNode *pParent); CPUTRenderNode *GetParent() { return mpParent; } CPUTRenderNode *GetChild() { return mpChild; } CPUTRenderNode *GetSibling() { return mpSibling; } cString &GetName() { return mName;}; cString &GetPrefix() { return mPrefix; } void SetPrefix( cString &prefix ) { mPrefix = prefix; } virtual bool IsModel() { return false; } float4x4 *GetParentMatrix() { return &mParentMatrix; } float4x4 *GetWorldMatrix(); void MarkDirty(); void AddChild(CPUTRenderNode *pNode); void AddSibling(CPUTRenderNode *pNode); virtual void Update( float deltaSeconds = 0.0f ){} virtual void UpdateRecursive( float deltaSeconds ); virtual void Render(CPUTRenderParameters &renderParams){} virtual void RenderShadow(CPUTRenderParameters &renderParams){} virtual void RenderRecursive(CPUTRenderParameters &renderParams); virtual void RenderShadowRecursive(CPUTRenderParameters &renderParams); void SetParentMatrix(const float4x4 &parentMatrix) { mParentMatrix = parentMatrix; MarkDirty(); } void LoadParentMatrixFromParameterBlock( CPUTConfigBlock *pBlock ) { // get and set the transform float pMatrix[4][4]; CPUTConfigEntry *pColumn = pBlock->GetValueByName(_L("matrixColumn0")); CPUTConfigEntry *pRow = pBlock->GetValueByName(_L("matrixRow0")); float4x4 parentMatrix; if( pColumn->IsValid() ) { pBlock->GetValueByName(_L("matrixColumn0"))->ValueAsFloatArray(&pMatrix[0][0], 4); pBlock->GetValueByName(_L("matrixColumn1"))->ValueAsFloatArray(&pMatrix[1][0], 4); pBlock->GetValueByName(_L("matrixColumn2"))->ValueAsFloatArray(&pMatrix[2][0], 4); pBlock->GetValueByName(_L("matrixColumn3"))->ValueAsFloatArray(&pMatrix[3][0], 4); parentMatrix = float4x4((float*)&pMatrix[0][0]); parentMatrix.transpose(); // Matrices are specified column-major, but consumed row-major. } else if( pRow->IsValid() ) { pBlock->GetValueByName(_L("matrixRow0"))->ValueAsFloatArray(&pMatrix[0][0], 4); pBlock->GetValueByName(_L("matrixRow1"))->ValueAsFloatArray(&pMatrix[1][0], 4); pBlock->GetValueByName(_L("matrixRow2"))->ValueAsFloatArray(&pMatrix[2][0], 4); pBlock->GetValueByName(_L("matrixRow3"))->ValueAsFloatArray(&pMatrix[3][0], 4); parentMatrix = float4x4((float*)&pMatrix[0][0]); } else { float identity[16] = { 1.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 1.f }; parentMatrix = float4x4(identity); } SetParentMatrix(parentMatrix); // set the relative transform, marking child transform's dirty } virtual void GetBoundingBoxRecursive( float3 *pCenter, float3 *pHalf) { if(mpChild) { mpChild->GetBoundingBoxRecursive( pCenter, pHalf ); } if(mpSibling) { mpSibling->GetBoundingBoxRecursive( pCenter, pHalf ); } } }; #endif // #ifndef __CPUTRENDERNODE_H__ ================================================ FILE: CPUT/CPUT/CPUTRenderParams.h ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #ifndef __CPUTRENDERPARAMS_H__ #define __CPUTRENDERPARAMS_H__ // TODO: Change name to CPUTRenderContext? class CPUTCamera; class CPUTRenderParameters { public: bool mShowBoundingBoxes; bool mDrawModels; bool mRenderOnlyVisibleModels; CPUTCamera *mpCamera; CPUTRenderParameters() : mShowBoundingBoxes(false), mDrawModels(true), mRenderOnlyVisibleModels(true), mpCamera(0) {} ~CPUTRenderParameters(){} private: }; #endif // __CPUTRENDERPARAMS_H__ ================================================ FILE: CPUT/CPUT/CPUTRenderParamsDX.h ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #ifndef __CPUTRENDERPARAMSDX_H__ #define __CPUTRENDERPARAMSDX_H__ #include "CPUT.h" #include #include #include "CPUTRenderParams.h" class CPUTRenderParametersDX : public CPUTRenderParameters { public: ID3D11DeviceContext *mpContext; public: CPUTRenderParametersDX(): mpContext(NULL){} CPUTRenderParametersDX( ID3D11DeviceContext *pContext, bool drawModels=true, bool renderOnlyVisibleModels=true, bool showBoundingBoxes=false ) : mpContext(pContext) { mShowBoundingBoxes = showBoundingBoxes; mDrawModels = drawModels; mRenderOnlyVisibleModels = renderOnlyVisibleModels; } ~CPUTRenderParametersDX(){} }; #endif // #ifndef __CPUTRENDERPARAMSDX_H__ ================================================ FILE: CPUT/CPUT/CPUTRenderStateBlock.cpp ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #include "CPUTAssetLibrary.h" #ifdef CPUT_FOR_DX11 #include "CPUTRenderStateBlockDX11.h" #else #error You must supply a target graphics API (ex: #define CPUT_FOR_DX11), or implement the target API for this file. #endif CPUTRenderStateBlock *CPUTRenderStateBlock::mpDefaultRenderStateBlock = NULL; CPUTRenderStateBlock *CPUTRenderStateBlock::CreateRenderStateBlock( const cString &name, const cString &absolutePathAndFilename ) { // TODO: Make this DX/OGL-API independent. How to choose which one gets created? Remember, we eventually want to support both in one app (to compare them with a keyboard toggle) #ifdef CPUT_FOR_DX11 CPUTRenderStateBlock *pRenderStateBlock = new CPUTRenderStateBlockDX11(); #else #error You must supply a target graphics API (ex: #define CPUT_FOR_DX11), or implement the target API for this file. #endif pRenderStateBlock->LoadRenderStateBlock( absolutePathAndFilename ); // add to library CPUTAssetLibrary::GetAssetLibrary()->AddRenderStateBlock( absolutePathAndFilename, pRenderStateBlock ); return pRenderStateBlock; } ================================================ FILE: CPUT/CPUT/CPUTRenderStateBlock.h ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #ifndef _CPUTRENDERSTATEBLOCK_H #define _CPUTRENDERSTATEBLOCK_H #include "CPUT.h" #include "CPUTRefCount.h" class CPUTRenderParameters; class CPUTRenderStateBlock : public CPUTRefCount { protected: static CPUTRenderStateBlock *mpDefaultRenderStateBlock; cString mMaterialName; ~CPUTRenderStateBlock(){} // Destructor is not public. Must release instead of delete. public: static CPUTRenderStateBlock *CreateRenderStateBlock( const cString &name, const cString &absolutePathAndFilename ); static CPUTRenderStateBlock *GetDefaultRenderStateBlock() { return mpDefaultRenderStateBlock; } static void SetDefaultRenderStateBlock( CPUTRenderStateBlock *pBlock ) { SAFE_RELEASE( mpDefaultRenderStateBlock ); mpDefaultRenderStateBlock = pBlock; } CPUTRenderStateBlock(){} virtual CPUTResult LoadRenderStateBlock(const cString &fileName) = 0; virtual void SetRenderStates(CPUTRenderParameters &renderParams) = 0; virtual void CreateNativeResources() = 0; }; #endif // _CPUTRENDERSTATEBLOCK_H ================================================ FILE: CPUT/CPUT/CPUTRenderStateBlockDX11.cpp ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #include "CPUT_DX11.h" #include "CPUTRenderStateBlockDX11.h" #include "CPUTRenderStateMapsDX11.h" //----------------------------------------------------------------------------- void CPUTRenderStateBlockDX11::ReadValue( CPUTConfigEntry *pValue, const CPUTRenderStateMapEntry *pRenderStateList, void *pDest ) { cString lowerCaseName = pValue->NameAsString(); bool found = false; // Find it in the map. TODO: could use a real map. Maybe with a binary search, lexical storage, etc. for( CPUTRenderStateMapEntry const *pCur = pRenderStateList; pCur->name.compare(_L("")); pCur++ ) { found = 0 == _wcsicmp( lowerCaseName.data(), pCur->name.data() ); if( found ) { // We found it. Now convert it from the text file's string to its internal representation // There must be a more-generic way to do the following. write( void*, void*, type ). // Use function pointer array to ValueAsInt() and similar, so we can call them without the switch? // Might require they all have same signature ==> use void pointers, and cast internally? switch( pCur->type ) { case ePARAM_TYPE_TYPELESS: ASSERT(0,_L("")); break; // Should not get here. case ePARAM_TYPE_INT: *(int*)&((char*)pDest)[pCur->offset] = pValue->ValueAsInt(); break; case ePARAM_TYPE_UINT: *(UINT*)&((char*)pDest)[pCur->offset] = pValue->ValueAsUint(); break; case ePARAM_TYPE_BOOL: *(bool*)&((char*)pDest)[pCur->offset] = pValue->ValueAsBool(); break; case ePARAM_TYPE_FLOAT: *(float*)&((char*)pDest)[pCur->offset] = pValue->ValueAsFloat(); break; case ePARAM_TYPE_SHORT: *(short*)&((char*)pDest)[pCur->offset] = pValue->ValueAsInt(); break; case ePARAM_TYPE_CHAR: ((char*)pDest)[pCur->offset] = pValue->ValueAsInt(); break; case ePARAM_TYPE_UCHAR: ((UCHAR*)pDest)[pCur->offset] = pValue->ValueAsUint(); break; case ePARAM_TYPE_STRING: break; // Not sure how to deal with this yet. Strings can have different sizes // The following types must be converted from string to enum. They achieve this with a translation map case ePARAM_TYPE_D3D11_BLEND: found = pBlendMap->FindMapEntryByName( (int*)&((char*)pDest)[pCur->offset], pValue->ValueAsString() ); break; case ePARAM_TYPE_D3D11_BLEND_OP: found = pBlendOpMap->FindMapEntryByName( (int*)&((char*)pDest)[pCur->offset], pValue->ValueAsString() ); break; case ePARAM_TYPE_DEPTH_WRITE_MASK: found = pDepthWriteMaskMap->FindMapEntryByName( (int*)&((char*)pDest)[pCur->offset], pValue->ValueAsString() ); break; case ePARAM_TYPE_D3D11_STENCIL_OP: found = pStencilOpMap->FindMapEntryByName( (int*)&((char*)pDest)[pCur->offset], pValue->ValueAsString() ); break; case ePARAM_TYPE_D3D11_FILL_MODE: found = pFillModeMap->FindMapEntryByName( (int*)&((char*)pDest)[pCur->offset], pValue->ValueAsString() ); break; case ePARAM_TYPE_D3D11_CULL_MODE: found = pCullModeMap->FindMapEntryByName( (int*)&((char*)pDest)[pCur->offset], pValue->ValueAsString() ); break; case ePARAM_TYPE_D3D11_FILTER: found = pFilterMap->FindMapEntryByName( (int*)&((char*)pDest)[pCur->offset], pValue->ValueAsString() ); break; case ePARAM_TYPE_D3D11_COMPARISON_FUNC: found = pComparisonMap->FindMapEntryByName( (int*)&((char*)pDest)[pCur->offset], pValue->ValueAsString() ); break; case ePARAM_TYPE_D3D11_TEXTURE_ADDRESS_MODE: found = pTextureAddressMap->FindMapEntryByName( (int*)&((char*)pDest)[pCur->offset], pValue->ValueAsString() ); break; } break; // From for. We found it, so we're done. } } ASSERT( found, _L( "Unkown render state: '") + pValue->NameAsString() + _L("'.") ); } // CPUTRenderStateBlockDX11::ReadValue() //----------------------------------------------------------------------------- CPUTResult CPUTRenderStateBlockDX11::ReadProperties( CPUTConfigFile &file, const cString &blockName, const CPUTRenderStateMapEntry *pMap, void *pDest ) { CPUTConfigBlock *pProperties = file.GetBlockByName(blockName); if( !pProperties ) { // Note: We choose not to assert here. The nature of the parameter block is that // only the values that deviate from default need to be present. It is very // common that blocks will be missing return CPUT_ERROR_PARAMETER_BLOCK_NOT_FOUND; } UINT count = pProperties->ValueCount(); for( UINT ii=0; iiGetValue(ii); ASSERT( pValue->IsValid(), _L("Invalid Value: '")+pValue->NameAsString()+_L("'.") ); ReadValue( pValue, pMap, pDest ); } return CPUT_SUCCESS; } // CPUTRenderStateBlockDX11::ReadProperties() //----------------------------------------------------------------------------- void CPUTRenderStateDX11::SetDefaults() { // TODO: it would be nice if we could just initialize this with struct initialization. UINT ii; BlendDesc.AlphaToCoverageEnable = FALSE; BlendDesc.IndependentBlendEnable = FALSE; for( ii=0; ii<8; ii++ ) { BlendDesc.RenderTarget[ii].BlendEnable = FALSE; BlendDesc.RenderTarget[ii].SrcBlend = D3D11_BLEND_ONE; BlendDesc.RenderTarget[ii].DestBlend = D3D11_BLEND_ZERO; BlendDesc.RenderTarget[ii].BlendOp = D3D11_BLEND_OP_ADD; BlendDesc.RenderTarget[ii].SrcBlendAlpha = D3D11_BLEND_ONE; BlendDesc.RenderTarget[ii].DestBlendAlpha = D3D11_BLEND_ZERO; BlendDesc.RenderTarget[ii].BlendOpAlpha = D3D11_BLEND_OP_ADD; BlendDesc.RenderTarget[ii].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; } BlendFactor[0] = BlendFactor[1] = BlendFactor[2] = BlendFactor[3] = 1.0f; SampleMask = 0xFFFFFFFF; DepthStencilDesc.DepthEnable = TRUE; DepthStencilDesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL; DepthStencilDesc.DepthFunc = D3D11_COMPARISON_GREATER_EQUAL; DepthStencilDesc.StencilEnable = FALSE; DepthStencilDesc.StencilReadMask = D3D11_DEFAULT_STENCIL_READ_MASK; DepthStencilDesc.StencilWriteMask = D3D11_DEFAULT_STENCIL_WRITE_MASK; DepthStencilDesc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS; DepthStencilDesc.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_KEEP; DepthStencilDesc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP; DepthStencilDesc.FrontFace.StencilFailOp = D3D11_STENCIL_OP_KEEP; DepthStencilDesc.BackFace.StencilFunc = D3D11_COMPARISON_ALWAYS; DepthStencilDesc.BackFace.StencilDepthFailOp = D3D11_STENCIL_OP_KEEP; DepthStencilDesc.BackFace.StencilPassOp = D3D11_STENCIL_OP_KEEP; DepthStencilDesc.BackFace.StencilFailOp = D3D11_STENCIL_OP_KEEP; RasterizerDesc.FillMode = D3D11_FILL_SOLID; RasterizerDesc.CullMode = D3D11_CULL_BACK; RasterizerDesc.FrontCounterClockwise = FALSE; RasterizerDesc.DepthBias = 0; RasterizerDesc.SlopeScaledDepthBias = 0.0f; RasterizerDesc.DepthBiasClamp = 0.0f; RasterizerDesc.DepthClipEnable = TRUE; RasterizerDesc.ScissorEnable = FALSE; RasterizerDesc.MultisampleEnable = TRUE; RasterizerDesc.AntialiasedLineEnable = FALSE; for( ii=0; iiCreateBlendState( &mStateDesc.BlendDesc, &mpBlendState ); ASSERT( SUCCEEDED(hr), _L("Failed to create blend state.") ); hr = pDevice->CreateDepthStencilState( &mStateDesc.DepthStencilDesc, &mpDepthStencilState ); ASSERT( SUCCEEDED(hr), _L("Failed to create depth stencil state.") ); hr = pDevice->CreateRasterizerState( &mStateDesc.RasterizerDesc, &mpRasterizerState ); ASSERT( SUCCEEDED(hr), _L("Failed to create rasterizer state.") ); // TODO: how to map samplers to shaders? // Each type can have different samplers assigned (VS, PS, GS, etc.) // How does DX treat them? 16 unified? or 16 each? // For now, just read 16 samplers, and set to all stages for( UINT ii=0; iiCreateSamplerState( &mStateDesc.SamplerDesc[ii], &mpSamplerState[ii] ); ASSERT( SUCCEEDED(hr), _L("Failed to create sampler state.") ); } } // CPUTRenderStateBlockDX11::CreateDXResources() //----------------------------------------------------------------------------- void CPUTRenderStateBlockDX11::SetRenderStates( CPUTRenderParameters &renderParams ) { ID3D11DeviceContext *pContext = ((CPUTRenderParametersDX*)&renderParams)->mpContext; pContext->OMSetBlendState( mpBlendState, mStateDesc.BlendFactor, mStateDesc.SampleMask ); pContext->OMSetDepthStencilState( mpDepthStencilState, 0 ); // TODO: read stecil ref from config file pContext->RSSetState( mpRasterizerState ); pContext->PSSetSamplers( 0, mNumSamplers, mpSamplerState ); pContext->VSSetSamplers( 0, mNumSamplers, mpSamplerState ); pContext->GSSetSamplers( 0, mNumSamplers, mpSamplerState ); } // CPUTRenderStateBlockDX11::SetRenderState() ================================================ FILE: CPUT/CPUT/CPUTRenderStateBlockDX11.h ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #ifndef _CPUTRENDERSTATEBLOCKDX11_H #define _CPUTRENDERSTATEBLOCKDX11_H #include "CPUTRenderStateBlock.h" #include "CPUTConfigBlock.h" // include all DX11 headers needed #include #include struct CPUTRenderStateMapEntry; class CPUTRenderParameters; //----------------------------------------------------------------------------- // TODO: Move to own file class CPUTRenderStateDX11 { public: D3D11_BLEND_DESC BlendDesc; D3D11_DEPTH_STENCIL_DESC DepthStencilDesc; D3D11_RASTERIZER_DESC RasterizerDesc; D3D11_SAMPLER_DESC SamplerDesc[D3D11_COMMONSHADER_SAMPLER_SLOT_COUNT]; float BlendFactor[4]; UINT SampleMask; CPUTRenderStateDX11() { SetDefaults(); } ~CPUTRenderStateDX11() {} // Destructor is not public. Must release instead of delete. void SetDefaults(); }; //----------------------------------------------------------------------------- class CPUTRenderStateBlockDX11:public CPUTRenderStateBlock { protected: // The state descriptor describes all of the states. // We read it in when creating assets. We keep it around in case we need to adjust and recreate. CPUTRenderStateDX11 mStateDesc; // Each of the native state objects. ID3D11BlendState *mpBlendState; ID3D11DepthStencilState *mpDepthStencilState; ID3D11RasterizerState *mpRasterizerState; ID3D11SamplerState *mpSamplerState[D3D11_COMMONSHADER_SAMPLER_SLOT_COUNT]; UINT mNumSamplers; // Destructor is not public. Must release instead of delete. ~CPUTRenderStateBlockDX11() { SAFE_RELEASE( mpBlendState ); SAFE_RELEASE( mpDepthStencilState ); SAFE_RELEASE( mpRasterizerState ); for( UINT ii=0; iimName.length() > 0; pEntry++ ){ if( 0 == _wcsicmp( pEntry->mName.data(), name.data() ) ){ *pValue = pEntry->mValue; return true; } } return false; } }; #endif // _CPUTRENDERSTATEBLOCKDX11_H ================================================ FILE: CPUT/CPUT/CPUTRenderStateMapsDX11.h ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #ifndef _CPUTRENDERSTATEMAPSDX11_H #define _CPUTRENDERSTATEMAPSDX11_H // TODO: Should this be in a cpp file instead of an h file? // We just put it here to get it out of the way // in the implementation file (CPUTRenderStateBlockDX11.cpp) //----------------------------------------------------------------------------- static const CPUTRenderStateMapEntry pBlendDescMap[] = { { _L("alphatocoverageenable"), ePARAM_TYPE_BOOL, (UINT)offsetof(D3D11_BLEND_DESC, AlphaToCoverageEnable)}, { _L("independentblendenable"), ePARAM_TYPE_BOOL, (UINT)offsetof(D3D11_BLEND_DESC, IndependentBlendEnable)}, { _L("blendfactor1"), ePARAM_TYPE_FLOAT, (UINT)offsetof(CPUTRenderStateDX11, BlendFactor[0] )}, { _L("blendfactor2"), ePARAM_TYPE_FLOAT, (UINT)offsetof(CPUTRenderStateDX11, BlendFactor[1] )}, { _L("blendfactor3"), ePARAM_TYPE_FLOAT, (UINT)offsetof(CPUTRenderStateDX11, BlendFactor[2] )}, { _L("blendfactor4"), ePARAM_TYPE_FLOAT, (UINT)offsetof(CPUTRenderStateDX11, BlendFactor[3] )}, { _L("samplemask"), ePARAM_TYPE_UINT, (UINT)offsetof(CPUTRenderStateDX11, SampleMask )}, { _L(""), ePARAM_TYPE_TYPELESS, 0} }; //----------------------------------------------------------------------------- static const CPUTRenderStateMapEntry pRenderTargetBlendDescMap[] = { { _L("blendenable"), ePARAM_TYPE_BOOL, (UINT)offsetof(D3D11_RENDER_TARGET_BLEND_DESC, BlendEnable )}, { _L("srcblend"), ePARAM_TYPE_D3D11_BLEND, (UINT)offsetof(D3D11_RENDER_TARGET_BLEND_DESC, SrcBlend )}, { _L("destblend"), ePARAM_TYPE_D3D11_BLEND, (UINT)offsetof(D3D11_RENDER_TARGET_BLEND_DESC, DestBlend )}, { _L("blendop"), ePARAM_TYPE_D3D11_BLEND_OP, (UINT)offsetof(D3D11_RENDER_TARGET_BLEND_DESC, BlendOp )}, { _L("srcblendalpha"), ePARAM_TYPE_D3D11_BLEND, (UINT)offsetof(D3D11_RENDER_TARGET_BLEND_DESC, SrcBlendAlpha )}, { _L("destblendalpha"), ePARAM_TYPE_D3D11_BLEND, (UINT)offsetof(D3D11_RENDER_TARGET_BLEND_DESC, DestBlendAlpha )}, { _L("blendopalpha"), ePARAM_TYPE_D3D11_BLEND_OP, (UINT)offsetof(D3D11_RENDER_TARGET_BLEND_DESC, BlendOpAlpha )}, { _L("rendertargetwritemask"), ePARAM_TYPE_UCHAR, (UINT)offsetof(D3D11_RENDER_TARGET_BLEND_DESC, RenderTargetWriteMask )}, { _L(""), ePARAM_TYPE_TYPELESS, 0} }; //----------------------------------------------------------------------------- static const CPUTRenderStateMapEntry pDepthStencilDescMap[] = { { _L("depthenable"), ePARAM_TYPE_BOOL, (UINT)offsetof(D3D11_DEPTH_STENCIL_DESC, DepthEnable)}, { _L("depthwritemask"), ePARAM_TYPE_DEPTH_WRITE_MASK, (UINT)offsetof(D3D11_DEPTH_STENCIL_DESC, DepthWriteMask)}, { _L("depthfunc"), ePARAM_TYPE_D3D11_COMPARISON_FUNC, (UINT)offsetof(D3D11_DEPTH_STENCIL_DESC, DepthFunc)}, { _L("StencilEnable"), ePARAM_TYPE_BOOL, (UINT)offsetof(D3D11_DEPTH_STENCIL_DESC, StencilEnable)}, { _L("StencilReadMask"), ePARAM_TYPE_UCHAR, (UINT)offsetof(D3D11_DEPTH_STENCIL_DESC, StencilWriteMask)}, { _L("StencilWriteMask"), ePARAM_TYPE_UCHAR, (UINT)offsetof(D3D11_DEPTH_STENCIL_DESC, DepthWriteMask)}, { _L("FrontFaceStencilFailOp"), ePARAM_TYPE_D3D11_STENCIL_OP, (UINT)offsetof(D3D11_DEPTH_STENCIL_DESC, FrontFace.StencilFailOp )}, { _L("FrontFaceStencilDepthFailOp"), ePARAM_TYPE_D3D11_STENCIL_OP, (UINT)offsetof(D3D11_DEPTH_STENCIL_DESC, FrontFace.StencilDepthFailOp)}, { _L("FrontFaceStencilPassOp"), ePARAM_TYPE_D3D11_STENCIL_OP, (UINT)offsetof(D3D11_DEPTH_STENCIL_DESC, FrontFace.StencilPassOp)}, { _L("FrontFaceStencilFunc"), ePARAM_TYPE_D3D11_COMPARISON_FUNC, (UINT)offsetof(D3D11_DEPTH_STENCIL_DESC, FrontFace.StencilFunc)}, { _L("BackFaceStencilFailOp"), ePARAM_TYPE_D3D11_STENCIL_OP, (UINT)offsetof(D3D11_DEPTH_STENCIL_DESC, BackFace.StencilDepthFailOp)}, { _L("BackFaceStencilDepthFailOp"), ePARAM_TYPE_D3D11_STENCIL_OP, (UINT)offsetof(D3D11_DEPTH_STENCIL_DESC, BackFace.StencilDepthFailOp)}, { _L("BackFaceStencilPassOp"), ePARAM_TYPE_D3D11_STENCIL_OP, (UINT)offsetof(D3D11_DEPTH_STENCIL_DESC, BackFace.StencilPassOp)}, { _L("BackFaceStencilFunc"), ePARAM_TYPE_D3D11_COMPARISON_FUNC, (UINT)offsetof(D3D11_DEPTH_STENCIL_DESC, BackFace.StencilFunc)}, { _L(""), ePARAM_TYPE_TYPELESS, 0} }; //----------------------------------------------------------------------------- static const CPUTRenderStateMapEntry pRasterizerDescMap[] = { { _L("FillMode"), ePARAM_TYPE_D3D11_FILL_MODE, (UINT)offsetof(D3D11_RASTERIZER_DESC, FillMode)}, { _L("CullMode"), ePARAM_TYPE_D3D11_CULL_MODE, (UINT)offsetof(D3D11_RASTERIZER_DESC, CullMode)}, { _L("FrontCounterClockwise"), ePARAM_TYPE_BOOL, (UINT)offsetof(D3D11_RASTERIZER_DESC, FrontCounterClockwise)}, { _L("DepthBias"), ePARAM_TYPE_INT, (UINT)offsetof(D3D11_RASTERIZER_DESC, DepthBias)}, { _L("DepthBiasClamp"), ePARAM_TYPE_FLOAT, (UINT)offsetof(D3D11_RASTERIZER_DESC, DepthBiasClamp)}, { _L("SlopeScaledDepthBias"), ePARAM_TYPE_FLOAT, (UINT)offsetof(D3D11_RASTERIZER_DESC, SlopeScaledDepthBias)}, { _L("DepthClipEnable"), ePARAM_TYPE_BOOL, (UINT)offsetof(D3D11_RASTERIZER_DESC, DepthClipEnable)}, { _L("ScissorEnable"), ePARAM_TYPE_BOOL, (UINT)offsetof(D3D11_RASTERIZER_DESC, ScissorEnable)}, { _L("MultisampleEnable"), ePARAM_TYPE_BOOL, (UINT)offsetof(D3D11_RASTERIZER_DESC, MultisampleEnable)}, { _L("AntialiasedLineEnable"), ePARAM_TYPE_BOOL, (UINT)offsetof(D3D11_RASTERIZER_DESC, AntialiasedLineEnable)}, { _L(""), ePARAM_TYPE_TYPELESS, 0} }; //----------------------------------------------------------------------------- static const CPUTRenderStateMapEntry pSamplerDescMap[] = { { _L("Filter"), ePARAM_TYPE_D3D11_FILTER, (UINT)offsetof(D3D11_SAMPLER_DESC, Filter)}, { _L("AddressU"), ePARAM_TYPE_D3D11_TEXTURE_ADDRESS_MODE, (UINT)offsetof(D3D11_SAMPLER_DESC, AddressU)}, { _L("AddressV"), ePARAM_TYPE_D3D11_TEXTURE_ADDRESS_MODE, (UINT)offsetof(D3D11_SAMPLER_DESC, AddressV)}, { _L("AddressW"), ePARAM_TYPE_D3D11_TEXTURE_ADDRESS_MODE, (UINT)offsetof(D3D11_SAMPLER_DESC, AddressW)}, { _L("MipLODBias"), ePARAM_TYPE_FLOAT, (UINT)offsetof(D3D11_SAMPLER_DESC, MipLODBias)}, { _L("MaxAnisotropy"), ePARAM_TYPE_UINT, (UINT)offsetof(D3D11_SAMPLER_DESC, MaxAnisotropy)}, { _L("ComparisonFunc"), ePARAM_TYPE_D3D11_COMPARISON_FUNC, (UINT)offsetof(D3D11_SAMPLER_DESC, ComparisonFunc)}, { _L("BorderColor0"), ePARAM_TYPE_FLOAT, (UINT)offsetof(D3D11_SAMPLER_DESC, BorderColor[0])}, { _L("BorderColor1"), ePARAM_TYPE_FLOAT, (UINT)offsetof(D3D11_SAMPLER_DESC, BorderColor[1])}, { _L("BorderColor2"), ePARAM_TYPE_FLOAT, (UINT)offsetof(D3D11_SAMPLER_DESC, BorderColor[2])}, { _L("BorderColor3"), ePARAM_TYPE_FLOAT, (UINT)offsetof(D3D11_SAMPLER_DESC, BorderColor[3])}, { _L("MinLOD"), ePARAM_TYPE_FLOAT, (UINT)offsetof(D3D11_SAMPLER_DESC, MinLOD)}, { _L("MaxLOD"), ePARAM_TYPE_FLOAT, (UINT)offsetof(D3D11_SAMPLER_DESC, MaxLOD)}, { _L(""), ePARAM_TYPE_TYPELESS, 0} }; //----------------------------------------------------------------------------- static const StringToIntMapEntry pBlendMap[] = { { _L("d3d11_blend_zero"), D3D11_BLEND_ZERO }, { _L("d3d11_blend_one"), D3D11_BLEND_ONE }, { _L("d3d11_blend_src_color"), D3D11_BLEND_SRC_COLOR }, { _L("d3d11_blend_inv_src_color"), D3D11_BLEND_INV_SRC_COLOR }, { _L("d3d11_blend_src_alpha"), D3D11_BLEND_SRC_ALPHA }, { _L("d3d11_blend_inv_src_alpha"), D3D11_BLEND_INV_SRC_ALPHA }, { _L("d3d11_blend_dest_alpha"), D3D11_BLEND_DEST_ALPHA }, { _L("d3d11_blend_inv_dest_alpha"), D3D11_BLEND_INV_DEST_ALPHA }, { _L("d3d11_blend_dest_color"), D3D11_BLEND_DEST_COLOR }, { _L("d3d11_blend_inv_dest_color"), D3D11_BLEND_INV_DEST_COLOR }, { _L("d3d11_blend_src_alpha_sat"), D3D11_BLEND_SRC_ALPHA_SAT }, { _L("d3d11_blend_blend_factor"), D3D11_BLEND_BLEND_FACTOR }, { _L("d3d11_blend_inv_blend_factor"), D3D11_BLEND_INV_BLEND_FACTOR }, { _L("d3d11_blend_src1_color"), D3D11_BLEND_SRC1_COLOR }, { _L("d3d11_blend_inv_src1_color"), D3D11_BLEND_INV_SRC1_COLOR }, { _L("d3d11_blend_src1_alpha"), D3D11_BLEND_SRC1_ALPHA }, { _L("d3d11_blend_inv_src1_alpha"), D3D11_BLEND_INV_SRC1_ALPHA }, { _L(""), -1 } }; //----------------------------------------------------------------------------- static const StringToIntMapEntry pBlendOpMap[] = { { _L("d3d11_blend_op_add"), D3D11_BLEND_OP_ADD }, { _L("d3d11_blend_op_subtract"), D3D11_BLEND_OP_SUBTRACT }, { _L("d3d11_blend_op_rev_subtract"), D3D11_BLEND_OP_REV_SUBTRACT }, { _L("d3d11_blend_op_min"), D3D11_BLEND_OP_MIN }, { _L("d3d11_blend_op_max"), D3D11_BLEND_OP_MAX }, { _L(""), -1 } }; //----------------------------------------------------------------------------- static const StringToIntMapEntry pDepthWriteMaskMap[] = { { _L("D3D11_DEPTH_WRITE_MASK_ZERO"), D3D11_DEPTH_WRITE_MASK_ZERO }, { _L("D3D11_DEPTH_WRITE_MASK_ALL"), D3D11_DEPTH_WRITE_MASK_ALL }, { _L(""), -1 } }; //----------------------------------------------------------------------------- static const StringToIntMapEntry pComparisonMap[] = { { _L("D3D11_COMPARISON_NEVER"), D3D11_COMPARISON_NEVER }, { _L("D3D11_COMPARISON_LESS"), D3D11_COMPARISON_LESS }, { _L("D3D11_COMPARISON_EQUAL"), D3D11_COMPARISON_EQUAL }, { _L("D3D11_COMPARISON_LESS_EQUAL"), D3D11_COMPARISON_LESS_EQUAL }, { _L("D3D11_COMPARISON_GREATER"), D3D11_COMPARISON_GREATER }, { _L("D3D11_COMPARISON_NOT_EQUAL"), D3D11_COMPARISON_NOT_EQUAL}, { _L("D3D11_COMPARISON_GREATER_EQUAL"), D3D11_COMPARISON_GREATER_EQUAL}, { _L("D3D11_COMPARISON_ALWAYS"), D3D11_COMPARISON_ALWAYS}, { _L(""), -1 } }; //----------------------------------------------------------------------------- static const StringToIntMapEntry pStencilOpMap[] = { { _L("D3D11_STENCIL_OP_KEEP"), D3D11_STENCIL_OP_KEEP }, { _L("D3D11_STENCIL_OP_ZERO"), D3D11_STENCIL_OP_ZERO }, { _L("D3D11_STENCIL_OP_REPLACE"), D3D11_STENCIL_OP_REPLACE }, { _L("D3D11_STENCIL_OP_INCR_SAT"), D3D11_STENCIL_OP_INCR_SAT }, { _L("D3D11_STENCIL_OP_DECR_SAT"), D3D11_STENCIL_OP_DECR_SAT }, { _L("D3D11_STENCIL_OP_INVERT"), D3D11_STENCIL_OP_INVERT }, { _L("D3D11_STENCIL_OP_INCR"), D3D11_STENCIL_OP_INCR }, { _L("D3D11_STENCIL_OP_DECR"), D3D11_STENCIL_OP_DECR }, { _L(""), -1 } }; //----------------------------------------------------------------------------- static const StringToIntMapEntry pFillModeMap[] = { { _L("D3D11_FILL_WIREFRAME"), D3D11_FILL_WIREFRAME }, { _L("D3D11_FILL_SOLID"), D3D11_FILL_SOLID }, { _L(""), -1 } }; //----------------------------------------------------------------------------- static const StringToIntMapEntry pCullModeMap[] = { { _L("D3D11_CULL_NONE"), D3D11_CULL_NONE }, { _L("D3D11_CULL_FRONT"), D3D11_CULL_FRONT }, { _L("D3D11_CULL_BACK"), D3D11_CULL_BACK }, { _L(""), -1 } }; //----------------------------------------------------------------------------- static const StringToIntMapEntry pFilterMap[] = { { _L("D3D11_FILTER_MIN_MAG_MIP_POINT"), D3D11_FILTER_MIN_MAG_MIP_POINT }, { _L("D3D11_FILTER_MIN_MAG_POINT_MIP_LINEAR"), D3D11_FILTER_MIN_MAG_POINT_MIP_LINEAR }, { _L("D3D11_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT"), D3D11_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT }, { _L("D3D11_FILTER_MIN_POINT_MAG_MIP_LINEAR"), D3D11_FILTER_MIN_POINT_MAG_MIP_LINEAR }, { _L("D3D11_FILTER_MIN_LINEAR_MAG_MIP_POINT"), D3D11_FILTER_MIN_LINEAR_MAG_MIP_POINT }, { _L("D3D11_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR"), D3D11_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR }, { _L("D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT"), D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT }, { _L("D3D11_FILTER_MIN_MAG_MIP_LINEAR"), D3D11_FILTER_MIN_MAG_MIP_LINEAR }, { _L("D3D11_FILTER_ANISOTROPIC"), D3D11_FILTER_ANISOTROPIC }, { _L("D3D11_FILTER_COMPARISON_MIN_MAG_MIP_POINT"), D3D11_FILTER_COMPARISON_MIN_MAG_MIP_POINT }, { _L("D3D11_FILTER_COMPARISON_MIN_MAG_POINT_MIP_LINEAR"), D3D11_FILTER_COMPARISON_MIN_MAG_POINT_MIP_LINEAR }, { _L("D3D11_FILTER_COMPARISON_MIN_POINT_MAG_LINEAR_MIP_POINT"), D3D11_FILTER_COMPARISON_MIN_POINT_MAG_LINEAR_MIP_POINT }, { _L("D3D11_FILTER_COMPARISON_MIN_POINT_MAG_MIP_LINEAR"), D3D11_FILTER_COMPARISON_MIN_POINT_MAG_MIP_LINEAR }, { _L("D3D11_FILTER_COMPARISON_MIN_LINEAR_MAG_MIP_POINT"), D3D11_FILTER_COMPARISON_MIN_LINEAR_MAG_MIP_POINT }, { _L("D3D11_FILTER_COMPARISON_MIN_LINEAR_MAG_POINT_MIP_LINEAR"), D3D11_FILTER_COMPARISON_MIN_LINEAR_MAG_POINT_MIP_LINEAR }, { _L("D3D11_FILTER_COMPARISON_MIN_MAG_LINEAR_MIP_POINT"), D3D11_FILTER_COMPARISON_MIN_MAG_LINEAR_MIP_POINT }, { _L("D3D11_FILTER_COMPARISON_MIN_MAG_MIP_LINEAR"), D3D11_FILTER_COMPARISON_MIN_MAG_MIP_LINEAR }, { _L("D3D11_FILTER_COMPARISON_ANISOTROPIC"), D3D11_FILTER_COMPARISON_ANISOTROPIC }, // { _L("D3D11_FILTER_TEXT_1BIT"), D3D11_FILTER_TEXT_1BIT }, // DX docs list this, but not in actual structure { _L(""), -1 } }; //----------------------------------------------------------------------------- static const StringToIntMapEntry pTextureAddressMap[] = { { _L("D3D11_TEXTURE_ADDRESS_WRAP"), D3D11_TEXTURE_ADDRESS_WRAP }, { _L("D3D11_TEXTURE_ADDRESS_MIRROR"), D3D11_TEXTURE_ADDRESS_MIRROR }, { _L("D3D11_TEXTURE_ADDRESS_CLAMP"), D3D11_TEXTURE_ADDRESS_CLAMP }, { _L("D3D11_TEXTURE_ADDRESS_BORDER"), D3D11_TEXTURE_ADDRESS_BORDER }, { _L("D3D11_TEXTURE_ADDRESS_MIRROR_ONCE"), D3D11_TEXTURE_ADDRESS_MIRROR_ONCE }, { _L(""), -1 } }; #endif //_CPUTRENDERSTATEMAPSDX11_H ================================================ FILE: CPUT/CPUT/CPUTRenderTarget.cpp ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #include "CPUT_DX11.h" #include "CPUTRenderTarget.h" #include "CPUTAssetLibrary.h" #include "CPUTMaterialDX11.h" #include "CPUTTextureDX11.h" #include "CPUTBufferDX11.h" UINT CPUTRenderTargetColor::sCurrentWidth = 0; UINT CPUTRenderTargetColor::sCurrentHeight = 0; UINT CPUTRenderTargetDepth::sCurrentWidth = 0; UINT CPUTRenderTargetDepth::sCurrentHeight = 0; ID3D11RenderTargetView *CPUTRenderTargetColor::spActiveRenderTargetView = NULL; ID3D11DepthStencilView *CPUTRenderTargetDepth::spActiveDepthStencilView = NULL; //----------------------------------------------- CPUTRenderTargetColor::~CPUTRenderTargetColor() { SAFE_RELEASE( mpColorTexture ); SAFE_RELEASE( mpColorUAV ); SAFE_RELEASE( mpColorBuffer ); SAFE_RELEASE( mpColorTextureDX ); SAFE_RELEASE( mpColorTextureDXMSAA ); SAFE_RELEASE( mpColorSRV ); SAFE_RELEASE( mpColorRenderTargetView ); SAFE_RELEASE( mpColorTextureMSAA ); SAFE_RELEASE( mpColorTextureDXMSAA ); SAFE_RELEASE( mpColorSRVMSAA ); SAFE_RELEASE( mpColorTextureDXStaging ); } //----------------------------------------------- CPUTRenderTargetDepth::~CPUTRenderTargetDepth() { SAFE_RELEASE( mpDepthTexture ); SAFE_RELEASE( mpDepthTextureDX ); SAFE_RELEASE( mpDepthResourceView ); SAFE_RELEASE( mpDepthStencilView ); } //----------------------------------------------- HRESULT CPUTRenderTargetColor::CreateRenderTarget( cString textureName, UINT width, UINT height, DXGI_FORMAT colorFormat, UINT multiSampleCount, bool createUAV, bool recreate ) { HRESULT result; mName = textureName; mWidth = width; mHeight = height; mColorFormat = colorFormat; mMultiSampleCount = multiSampleCount; CPUTAssetLibrary *pAssetLibrary = CPUTAssetLibrary::GetAssetLibrary(); CPUTOSServices *pServices = CPUTOSServices::GetOSServices(); // Create the color texture mColorDesc = CD3D11_TEXTURE2D_DESC( colorFormat, width, height, 1, // Array Size 1, // MIP Levels D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_UNORDERED_ACCESS, D3D11_USAGE_DEFAULT, 0, mMultiSampleCount, 0 ); ID3D11Device *pD3dDevice = CPUT_DX11::GetDevice(); // If MSAA enabled, then create MSAA texture if( mMultiSampleCount>1 ) { result = pD3dDevice->CreateTexture2D( &mColorDesc, NULL, &mpColorTextureDXMSAA ); ASSERT( SUCCEEDED(result), _L("Failed creating MSAA render target texture") ); D3D11_SHADER_RESOURCE_VIEW_DESC srDesc = { colorFormat, D3D11_SRV_DIMENSION_TEXTURE2DMS, 0 }; srDesc.Texture2D.MipLevels = 1; result = pD3dDevice->CreateShaderResourceView( mpColorTextureDXMSAA, &srDesc, &mpColorSRVMSAA ); ASSERT( SUCCEEDED(result), _L("Failed creating MSAA render target shader resource view") ); CPUTSetDebugName( mpColorSRVMSAA, textureName + _L(" ColorMSAA") ); if( !recreate ) { cString msaaName = mName + _L("_MSAA"); mpColorTextureMSAA = new CPUTTextureDX11( msaaName ); // If the name starts with a '$', then its an internal texture (has no filesystem path). // Otherwise, its a file, so prepend filesystem path CPUTAssetLibrary *pAssetLibrary = CPUTAssetLibrary::GetAssetLibrary(); cString finalName; if( mName.at(0) == '$' ) { finalName = msaaName; }else { pServices->ResolveAbsolutePathAndFilename( (pAssetLibrary->GetTextureDirectory() + msaaName), &finalName); } pAssetLibrary->AddTexture( finalName, mpColorTextureMSAA ); } ((CPUTTextureDX11*)mpColorTextureMSAA)->SetTextureAndShaderResourceView( mpColorTextureDXMSAA, mpColorSRVMSAA ); } // Create non-MSAA texture. If we're MSAA, then we'll resolve into this. If not, then we'll render directly to this one. mColorDesc.SampleDesc.Count = 1; result = pD3dDevice->CreateTexture2D( &mColorDesc, NULL, &mpColorTextureDX ); ASSERT( SUCCEEDED(result), _L("Failed creating render target texture") ); // Create the shader-resource view from the non-MSAA texture D3D11_SHADER_RESOURCE_VIEW_DESC srDesc = { colorFormat, D3D11_SRV_DIMENSION_TEXTURE2D, 0 }; srDesc.Texture2D.MipLevels = 1; result = pD3dDevice->CreateShaderResourceView( mpColorTextureDX, &srDesc, &mpColorSRV ); ASSERT( SUCCEEDED(result), _L("Failed creating render target shader resource view") ); CPUTSetDebugName( mpColorSRV, textureName + _L(" Color") ); mHasUav = createUAV; // Remember, so we know to recreate it (or not) on RecreateRenderTarget() if( createUAV ) { // D3D11_SHADER_RESOURCE_VIEW_DESC srDesc = { colorFormat, D3D_SRV_DIMENSION_BUFFER, 0 }; D3D11_UNORDERED_ACCESS_VIEW_DESC uavDesc; memset( &uavDesc, 0, sizeof(uavDesc) ); uavDesc.Format = colorFormat; uavDesc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2D; uavDesc.Texture2D.MipSlice = 0; result = pD3dDevice->CreateUnorderedAccessView( mpColorTextureDX, &uavDesc, &mpColorUAV ); ASSERT( SUCCEEDED(result), _L("Failed creating render target buffer shader resource view") ); CPUTSetDebugName( mpColorUAV, textureName + _L(" Color Buffer") ); } if( !recreate ) { mpColorTexture = new CPUTTextureDX11(mName); pAssetLibrary->AddTexture( mName, mpColorTexture ); mpColorBuffer = new CPUTBufferDX11(mName, NULL, mpColorUAV); // We don't have an ID3D11Buffer, but we want to track this UAV as if the texture was a buffer. pAssetLibrary->AddBuffer( mName, mpColorBuffer ); } ((CPUTTextureDX11*)mpColorTexture)->SetTextureAndShaderResourceView( mpColorTextureDX, mpColorSRV); // Choose our render target. If MSAA, then use the MSAA texture, and use resolve to fill the non-MSAA texture. ID3D11Texture2D *pColorTexture = (mMultiSampleCount>1) ? mpColorTextureDXMSAA : mpColorTextureDX; result = pD3dDevice->CreateRenderTargetView( pColorTexture, NULL, &mpColorRenderTargetView ); ASSERT( SUCCEEDED(result), _L("Failed creating render target view") ); CPUTSetDebugName( mpColorRenderTargetView, mName ); return S_OK; } //----------------------------------------------- HRESULT CPUTRenderTargetColor::RecreateRenderTarget( UINT width, UINT height, DXGI_FORMAT colorFormat, UINT multiSampleCount ) { // We don't release these. Instead, we release their resource views, and change them to the newly-created versions. // SAFE_RELEASE( mpColorTexture ); SAFE_RELEASE( mpColorTextureDX ); SAFE_RELEASE( mpColorSRV ); SAFE_RELEASE( mpColorRenderTargetView ); SAFE_RELEASE( mpColorTextureDXMSAA ); SAFE_RELEASE( mpColorSRVMSAA ); // TODO: Complete buffer changes by including them here. // Do not release saved resource views since they are not addreff'ed //SAFE_RELEASE( mpSavedColorRenderTargetView ); //SAFE_RELEASE( mpSavedDepthStencilView ); return CreateRenderTarget( mName, width, height, (DXGI_FORMAT_UNKNOWN != colorFormat) ? colorFormat : mColorFormat, multiSampleCount, mHasUav, true ); } //----------------------------------------------- HRESULT CPUTRenderTargetDepth::CreateRenderTarget( cString textureName, UINT width, UINT height, DXGI_FORMAT depthFormat, UINT multiSampleCount, bool recreate ) { HRESULT result; mName = textureName; mWidth = width; mHeight = height; mDepthFormat = depthFormat; mMultiSampleCount = multiSampleCount; // NOTE: The following doesn't work for DX10.0 devices. // They don't support binding an MSAA depth texture as // If we have a DX 10.1 or no MSAA, then create a shader resource view, and add a CPUTTexture to the AssetLibrary D3D_FEATURE_LEVEL featureLevel = gpSample->GetFeatureLevel(); bool supportsResourceView = ( featureLevel >= D3D_FEATURE_LEVEL_10_1) || (mMultiSampleCount==1); D3D11_TEXTURE2D_DESC depthDesc = { width, height, 1, // MIP Levels 1, // Array Size DXGI_FORMAT(depthFormat - 1), // DXGI_FORMAT_R32_TYPELESS mMultiSampleCount, 0, D3D11_USAGE_DEFAULT, D3D11_BIND_DEPTH_STENCIL | (supportsResourceView ? D3D11_BIND_SHADER_RESOURCE : 0), 0, // CPU Access flags 0 // Misc flags }; // Create either a Texture2D, or Texture2DMS, depending on multisample count. D3D11_DSV_DIMENSION dsvDimension = (mMultiSampleCount>1) ? D3D11_DSV_DIMENSION_TEXTURE2DMS : D3D11_DSV_DIMENSION_TEXTURE2D; ID3D11Device *pD3dDevice = CPUT_DX11::GetDevice(); result = pD3dDevice->CreateTexture2D( &depthDesc, NULL, &mpDepthTextureDX ); ASSERT( SUCCEEDED(result), _L("Failed creating depth texture.\nAre you using MSAA with a DX10.0 GPU?\nOnly DX10.1 and above can create a shader resource view for an MSAA depth texture.") ); D3D11_DEPTH_STENCIL_VIEW_DESC dsvd = { depthFormat, dsvDimension, 0 }; result = pD3dDevice->CreateDepthStencilView( mpDepthTextureDX, &dsvd, &mpDepthStencilView ); ASSERT( SUCCEEDED(result), _L("Failed creating depth stencil view") ); CPUTSetDebugName( mpDepthStencilView, mName ); if( supportsResourceView ) { D3D11_SRV_DIMENSION srvDimension = (mMultiSampleCount>1) ? D3D11_SRV_DIMENSION_TEXTURE2DMS : D3D11_SRV_DIMENSION_TEXTURE2D; // Create the shader-resource view D3D11_SHADER_RESOURCE_VIEW_DESC depthRsDesc = { DXGI_FORMAT(depthFormat + 1), srvDimension, 0 }; // TODO: Support optionally creating MIP chain. Then, support MIP generation (e.g., GenerateMIPS()). depthRsDesc.Texture2D.MipLevels = 1; result = pD3dDevice->CreateShaderResourceView( mpDepthTextureDX, &depthRsDesc, &mpDepthResourceView ); ASSERT( SUCCEEDED(result), _L("Failed creating render target shader resource view") ); CPUTSetDebugName( mpDepthResourceView, textureName + _L(" Depth") ); if( !recreate ) { CPUTAssetLibrary *pAssetLibrary = CPUTAssetLibrary::GetAssetLibrary(); mpDepthTexture = new CPUTTextureDX11(mName); pAssetLibrary->AddTexture( mName, mpDepthTexture ); } ((CPUTTextureDX11*)mpDepthTexture)->SetTextureAndShaderResourceView(mpDepthTextureDX, mpDepthResourceView); } return S_OK; } //----------------------------------------------- HRESULT CPUTRenderTargetDepth::RecreateRenderTarget( UINT width, UINT height, DXGI_FORMAT depthFormat, UINT multiSampleCount ) { // We don't release these. Instead, we release their resource views, and change them to the newly-created versions. //SAFE_RELEASE( mpDepthTexture ); SAFE_RELEASE( mpDepthTextureDX ); SAFE_RELEASE( mpDepthResourceView ); SAFE_RELEASE( mpDepthStencilView ); // Do not release saved resource views since they are not addreff'ed //SAFE_RELEASE( mpSavedColorRenderTargetView ); //SAFE_RELEASE( mpSavedDepthStencilView ); return CreateRenderTarget( mName, width, height, (DXGI_FORMAT_UNKNOWN != depthFormat) ? depthFormat : mDepthFormat, multiSampleCount, true ); } //----------------------------------------------- void CPUTRenderTargetColor::SetRenderTarget( CPUTRenderParameters &renderParams, CPUTRenderTargetDepth *pDepthBuffer, DWORD renderTargetIndex, const float *pClearColor, bool clear, float zClearVal ) { // **************************** // Save the current render target "state" so we can restore it later. // **************************** mSavedWidth = CPUTRenderTargetColor::sCurrentWidth; mSavedHeight = CPUTRenderTargetColor::sCurrentHeight; // Save the render target view so we can restore it later. mpSavedColorRenderTargetView = CPUTRenderTargetColor::GetActiveRenderTargetView(); mpSavedDepthStencilView = CPUTRenderTargetDepth::GetActiveDepthStencilView(); CPUTRenderTargetColor::SetActiveWidthHeight( mWidth, mHeight ); CPUTRenderTargetDepth::SetActiveWidthHeight( mWidth, mHeight ); // TODO: support multiple render target views (i.e., MRT) ID3D11DeviceContext *pContext = ((CPUTRenderParametersDX*)&renderParams)->mpContext; // Make sure this render target isn't currently bound as a texture. static ID3D11ShaderResourceView *pSRV[16] = {0}; pContext->PSSetShaderResources( 0, 16, pSRV ); // Clear the shader resources to avoid a hazard warning ID3D11ShaderResourceView *pNullResources[16] = {0}; pContext->PSSetShaderResources(0, 16, pNullResources ); pContext->VSSetShaderResources(0, 16, pNullResources ); // **************************** // Set the new render target states // **************************** ID3D11DepthStencilView *pDepthStencilView = pDepthBuffer ? pDepthBuffer->GetDepthBufferView() : NULL; pContext->OMSetRenderTargets( 1, &mpColorRenderTargetView, pDepthStencilView ); CPUTRenderTargetColor::SetActiveRenderTargetView(mpColorRenderTargetView); CPUTRenderTargetDepth::SetActiveDepthStencilView(pDepthStencilView); if( clear ) { pContext->ClearRenderTargetView( mpColorRenderTargetView, pClearColor ); if( pDepthStencilView ) { pContext->ClearDepthStencilView( pDepthStencilView, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, zClearVal, 0 ); } } D3D11_VIEWPORT viewport = { 0.0f, 0.0f, (float)mWidth, (float)mHeight, 0.0f, 1.0f }; ((CPUTRenderParametersDX*)&renderParams)->mpContext->RSSetViewports( 1, &viewport ); mRenderTargetSet = true; CPUTMaterialDX11::ResetStateTracking(); } // CPUTRenderTargetColor::SetRenderTarget() //----------------------------------------------- void CPUTRenderTargetDepth::SetRenderTarget( CPUTRenderParameters &renderParams, DWORD renderTargetIndex, float zClearVal, bool clear ) { // **************************** // Save the current render target "state" so we can restore it later. // **************************** mSavedWidth = CPUTRenderTargetDepth::GetActiveWidth(); mSavedHeight = CPUTRenderTargetDepth::GetActiveHeight(); CPUTRenderTargetColor::SetActiveWidthHeight( mWidth, mHeight ); CPUTRenderTargetDepth::SetActiveWidthHeight( mWidth, mHeight ); // TODO: support multiple render target views (i.e., MRT) ID3D11DeviceContext *pContext = ((CPUTRenderParametersDX*)&renderParams)->mpContext; // Make sure this render target isn't currently bound as a texture. static ID3D11ShaderResourceView *pSRV[16] = {0}; pContext->PSSetShaderResources( 0, 16, pSRV ); // Save the color and depth views so we can restore them later. mpSavedColorRenderTargetView = CPUTRenderTargetColor::GetActiveRenderTargetView(); mpSavedDepthStencilView = CPUTRenderTargetDepth::GetActiveDepthStencilView(); // Clear the shader resources to avoid a hazard warning ID3D11ShaderResourceView *pNullResources[16] = {0}; pContext->PSSetShaderResources(0, 16, pNullResources ); pContext->VSSetShaderResources(0, 16, pNullResources ); // **************************** // Set the new render target states // **************************** ID3D11RenderTargetView *pView[1] = {NULL}; pContext->OMSetRenderTargets( 1, pView, mpDepthStencilView ); CPUTRenderTargetColor::SetActiveRenderTargetView( NULL ); CPUTRenderTargetDepth::SetActiveDepthStencilView( mpDepthStencilView ); if( clear ) { pContext->ClearDepthStencilView( mpDepthStencilView, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, zClearVal, 0 ); } D3D11_VIEWPORT viewport = { 0.0f, 0.0f, (float)mWidth, (float)mHeight, 0.0f, 1.0f }; ((CPUTRenderParametersDX*)&renderParams)->mpContext->RSSetViewports( 1, &viewport ); mRenderTargetSet = true; } // CPUTRenderTargetDepth::SetRenderTarget() //----------------------------------------- void CPUTRenderTargetColor::Resolve( CPUTRenderParameters &renderParams ) { ID3D11DeviceContext *pContext = ((CPUTRenderParametersDX*)&renderParams)->mpContext; pContext->ResolveSubresource( mpColorTextureDX, 0, mpColorTextureDXMSAA, 0, mColorFormat); } //----------------------------------------- void CPUTRenderTargetColor::RestoreRenderTarget( CPUTRenderParameters &renderParams ) { ASSERT( mRenderTargetSet, _L("Render target restored without calling SetRenderTarget()")); if( mMultiSampleCount>1 ) { Resolve( renderParams ); } ID3D11DeviceContext *pContext = ((CPUTRenderParametersDX*)&renderParams)->mpContext; pContext->OMSetRenderTargets( 1, &mpSavedColorRenderTargetView, mpSavedDepthStencilView ); CPUTRenderTargetColor::SetActiveWidthHeight( mSavedWidth, mSavedHeight ); CPUTRenderTargetDepth::SetActiveWidthHeight( mSavedWidth, mSavedHeight ); CPUTRenderTargetColor::SetActiveRenderTargetView( mpSavedColorRenderTargetView ); CPUTRenderTargetDepth::SetActiveDepthStencilView( mpSavedDepthStencilView ); // TODO: save/restore original VIEWPORT settings, not assume full-screen viewport. D3D11_VIEWPORT viewport = { 0.0f, 0.0f, (float)mSavedWidth, (float)mSavedHeight, 0.0f, 1.0f }; ((CPUTRenderParametersDX*)&renderParams)->mpContext->RSSetViewports( 1, &viewport ); mRenderTargetSet = false; } // CPUTRenderTarget::RestoreRenderTarget() //----------------------------------------- // TODO: This function is exactly the same as the RestoreRenderTargetColor(). void CPUTRenderTargetDepth::RestoreRenderTarget( CPUTRenderParameters &renderParams ) { ASSERT( mRenderTargetSet, _L("Render target restored without calling SetRenderTarget()")); ID3D11DeviceContext *pContext = ((CPUTRenderParametersDX*)&renderParams)->mpContext; pContext->OMSetRenderTargets( 1, &mpSavedColorRenderTargetView, mpSavedDepthStencilView ); CPUTRenderTargetColor::SetActiveWidthHeight( mSavedWidth, mSavedHeight ); CPUTRenderTargetDepth::SetActiveWidthHeight( mSavedWidth, mSavedHeight ); CPUTRenderTargetColor::SetActiveRenderTargetView( mpSavedColorRenderTargetView ); CPUTRenderTargetDepth::SetActiveDepthStencilView( mpSavedDepthStencilView ); // TODO: save/restore original VIEWPORT settings, not assume full-screen viewport. D3D11_VIEWPORT viewport = { 0.0f, 0.0f, (float)mSavedWidth, (float)mSavedHeight, 0.0f, 1.0f }; ((CPUTRenderParametersDX*)&renderParams)->mpContext->RSSetViewports( 1, &viewport ); mRenderTargetSet = false; } // CPUTRenderTarget::RestoreRenderTarget() //----------------------------------------------------------------------------- D3D11_MAPPED_SUBRESOURCE CPUTRenderTargetColor::MapRenderTarget( CPUTRenderParameters ¶ms, eCPUTMapType type, bool wait ) { // Mapping for DISCARD requires dynamic buffer. Create dynamic copy? // Could easily provide input flag. But, where would we specify? Don't like specifying in the .set file // Because mapping is something the application wants to do - it isn't inherent in the data. // Could do Clone() and pass dynamic flag to that. // But, then we have two. Could always delete the other. // Could support programatic flag - apply to all loaded models in the .set // Could support programatic flag on model. Load model first, then load set. // For now, simply support CopyResource mechanism. HRESULT hr; ID3D11Device *pD3dDevice = CPUT_DX11::GetDevice(); CPUTRenderParametersDX *pParamsDX11 = (CPUTRenderParametersDX*)¶ms; ID3D11DeviceContext *pContext = pParamsDX11->mpContext; if( !mpColorTextureDXStaging ) { CD3D11_TEXTURE2D_DESC desc = mColorDesc; // First time. Create the staging resource desc.Usage = D3D11_USAGE_STAGING; switch( type ) { case CPUT_MAP_READ: desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; desc.BindFlags = 0; break; case CPUT_MAP_READ_WRITE: desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE; desc.BindFlags = 0; break; case CPUT_MAP_WRITE: case CPUT_MAP_WRITE_DISCARD: case CPUT_MAP_NO_OVERWRITE: desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; desc.BindFlags = 0; break; }; HRESULT hr = pD3dDevice->CreateTexture2D( &desc, NULL, &mpColorTextureDXStaging ); ASSERT( SUCCEEDED(hr), _L("Failed to create staging texture") ); CPUTSetDebugName( mpColorTextureDXStaging, _L("Staging buffer") ); } else { ASSERT( mMappedType == type, _L("Mapping with a different CPU access than creation parameter.") ); } D3D11_MAPPED_SUBRESOURCE info; switch( type ) { case CPUT_MAP_READ: case CPUT_MAP_READ_WRITE: // TODO: Copying and immediately mapping probably introduces a stall. // Expose the copy externally? // TODO: copy only if vb has changed? // Copy only first time? // Copy the GPU version before we read from it. pContext->CopyResource( mpColorTextureDXStaging, (mMultiSampleCount>1) ? mpColorTextureDXMSAA : mpColorTextureDX); break; }; hr = pContext->Map( mpColorTextureDXStaging, wait ? 0 : D3D11_MAP_FLAG_DO_NOT_WAIT, (D3D11_MAP)type, 0, &info ); mMappedType = type; return info; } // CPUTRenderTargetColor::Map() //----------------------------------------------------------------------------- void CPUTRenderTargetColor::UnmapRenderTarget( CPUTRenderParameters ¶ms ) { ASSERT( mMappedType != CPUT_MAP_UNDEFINED, _L("Can't unmap a render target that isn't mapped.") ); CPUTRenderParametersDX *pParamsDX11 = (CPUTRenderParametersDX*)¶ms; ID3D11DeviceContext *pContext = pParamsDX11->mpContext; pContext->Unmap( mpColorTextureDXStaging, 0 ); // If we were mapped for write, then copy staging buffer to GPU switch( mMappedType ) { case CPUT_MAP_READ: break; case CPUT_MAP_READ_WRITE: case CPUT_MAP_WRITE: case CPUT_MAP_WRITE_DISCARD: case CPUT_MAP_NO_OVERWRITE: pContext->CopyResource( mpColorTextureDX, mpColorTextureDXStaging ); break; }; // mMappedType = CPUT_MAP_UNDEFINED; } // CPUTRenderTargetColor::Unmap() //----------------------------------------------------------------------------- D3D11_MAPPED_SUBRESOURCE CPUTRenderTargetDepth::MapRenderTarget( CPUTRenderParameters ¶ms, eCPUTMapType type, bool wait ) { // Mapping for DISCARD requires dynamic buffer. Create dynamic copy? // Could easily provide input flag. But, where would we specify? Don't like specifying in the .set file // Because mapping is something the application wants to do - it isn't inherent in the data. // Could do Clone() and pass dynamic flag to that. // But, then we have two. Could always delete the other. // Could support programatic flag - apply to all loaded models in the .set // Could support programatic flag on model. Load model first, then load set. // For now, simply support CopyResource mechanism. HRESULT hr; ID3D11Device *pD3dDevice = CPUT_DX11::GetDevice(); CPUTRenderParametersDX *pParamsDX11 = (CPUTRenderParametersDX*)¶ms; ID3D11DeviceContext *pContext = pParamsDX11->mpContext; if( !mpDepthTextureDXStaging ) { CD3D11_TEXTURE2D_DESC desc = mDepthDesc; // First time. Create the staging resource desc.Usage = D3D11_USAGE_STAGING; switch( type ) { case CPUT_MAP_READ: desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; desc.BindFlags = 0; break; case CPUT_MAP_READ_WRITE: desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE; desc.BindFlags = 0; break; case CPUT_MAP_WRITE: case CPUT_MAP_WRITE_DISCARD: case CPUT_MAP_NO_OVERWRITE: desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; desc.BindFlags = 0; break; }; HRESULT hr = pD3dDevice->CreateTexture2D( &desc, NULL, &mpDepthTextureDXStaging ); ASSERT( SUCCEEDED(hr), _L("Failed to create staging texture") ); CPUTSetDebugName( mpDepthTextureDXStaging, _L("Staging buffer") ); } else { ASSERT( mMappedType == type, _L("Mapping with a different CPU access than creation parameter.") ); } D3D11_MAPPED_SUBRESOURCE info; switch( type ) { case CPUT_MAP_READ: case CPUT_MAP_READ_WRITE: // TODO: Copying and immediately mapping probably introduces a stall. // Expose the copy externally? // TODO: copy only if vb has changed? // Copy only first time? // Copy the GPU version before we read from it. // pContext->CopyResource( mpDepthTextureDXStaging, (mMultiSampleCount>1) ? mpDepthTextureDXMSAA : mpDepthTextureDX); // TODO: Do we (can we) support MSAA depth pContext->CopyResource( mpDepthTextureDXStaging, mpDepthTextureDX ); break; }; hr = pContext->Map( mpDepthTextureDXStaging, wait ? 0 : D3D11_MAP_FLAG_DO_NOT_WAIT, (D3D11_MAP)type, 0, &info ); mMappedType = type; return info; } // CPUTRenderTargetDepth::Map() //----------------------------------------------------------------------------- void CPUTRenderTargetDepth::UnmapRenderTarget( CPUTRenderParameters ¶ms ) { ASSERT( mMappedType != CPUT_MAP_UNDEFINED, _L("Can't unmap a render target that isn't mapped.") ); CPUTRenderParametersDX *pParamsDX11 = (CPUTRenderParametersDX*)¶ms; ID3D11DeviceContext *pContext = pParamsDX11->mpContext; pContext->Unmap( mpDepthTextureDXStaging, 0 ); // If we were mapped for write, then copy staging buffer to GPU switch( mMappedType ) { case CPUT_MAP_READ: break; case CPUT_MAP_READ_WRITE: case CPUT_MAP_WRITE: case CPUT_MAP_WRITE_DISCARD: case CPUT_MAP_NO_OVERWRITE: pContext->CopyResource( mpDepthTextureDX, mpDepthTextureDXStaging ); break; }; // mMappedType = CPUT_MAP_UNDEFINED; } // CPUTRenderTargetDepth::Unmap() ================================================ FILE: CPUT/CPUT/CPUTRenderTarget.h ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #ifndef _CPUTRENDERTARGET_H #define _CPUTRENDERTARGET_H #include "CPUT.h" #include "d3d11.h" class CPUTMaterial; class CPUTRenderParameters; class CPUTTexture; class CPUTBuffer; class CPUTRenderTargetDepth; class CPUTRenderTargetColor { public: static ID3D11RenderTargetView *GetActiveRenderTargetView() { return spActiveRenderTargetView; } static void SetActiveRenderTargetView(ID3D11RenderTargetView *pView) { spActiveRenderTargetView = pView; } static void SetActiveWidthHeight( UINT width, UINT height ) {sCurrentWidth = width; sCurrentHeight=height; } static UINT GetActiveWidth() {return sCurrentWidth; } static UINT GetActiveHeight() {return sCurrentHeight; } protected: static UINT sCurrentWidth; static UINT sCurrentHeight; static ID3D11RenderTargetView *spActiveRenderTargetView; cString mName; UINT mWidth; UINT mHeight; bool mRenderTargetSet; bool mHasUav; DXGI_FORMAT mColorFormat; UINT mMultiSampleCount; CD3D11_TEXTURE2D_DESC mColorDesc; CPUTTexture *mpColorTexture; CPUTBuffer *mpColorBuffer; ID3D11Texture2D *mpColorTextureDX; ID3D11ShaderResourceView *mpColorSRV; ID3D11UnorderedAccessView *mpColorUAV; ID3D11RenderTargetView *mpColorRenderTargetView; CPUTTexture *mpColorTextureMSAA; ID3D11Texture2D *mpColorTextureDXMSAA; ID3D11ShaderResourceView *mpColorSRVMSAA; ID3D11Texture2D *mpColorTextureDXStaging; eCPUTMapType mMappedType; UINT mSavedWidth; UINT mSavedHeight; ID3D11RenderTargetView *mpSavedColorRenderTargetView; ID3D11DepthStencilView *mpSavedDepthStencilView; public: CPUTRenderTargetColor() : mWidth(0), mHeight(0), mRenderTargetSet(false), mHasUav(false), mColorFormat(DXGI_FORMAT_UNKNOWN), mMultiSampleCount(1), mpColorTexture(NULL), mpColorBuffer(NULL), mpColorTextureDX(NULL), mpColorTextureDXMSAA(NULL), mpColorTextureMSAA(NULL), mpColorSRV(NULL), mpColorUAV(NULL), mpColorSRVMSAA(NULL), mpColorTextureDXStaging(NULL), mpColorRenderTargetView(NULL), mSavedWidth(0), mSavedHeight(0), mpSavedColorRenderTargetView(NULL), mpSavedDepthStencilView(NULL) { } ~CPUTRenderTargetColor(); HRESULT CreateRenderTarget( cString textureName, UINT width, UINT height, DXGI_FORMAT colorFormat, UINT multiSampleCount = 1, bool createUAV = false, bool recreate = false ); HRESULT RecreateRenderTarget( UINT width, UINT height, DXGI_FORMAT colorFormat = DXGI_FORMAT_UNKNOWN, UINT multiSampleCount = 1 ); void SetRenderTarget( CPUTRenderParameters &renderParams, CPUTRenderTargetDepth *pDepthBuffer = NULL, DWORD renderTargetIndex=0, const float *pClearColor=NULL, bool clear = false, float zClearVal = 0.0f ); void RestoreRenderTarget( CPUTRenderParameters &renderParams ); void Resolve( CPUTRenderParameters &renderParams ); CPUTTexture *GetColorTexture() { return mpColorTexture; } CPUTBuffer *GetColorBuffer() { return mpColorBuffer; } ID3D11ShaderResourceView *GetColorResourceView() { return mpColorSRV; } ID3D11UnorderedAccessView *GetColorUAV() { return mpColorUAV; } UINT GetWidth() { return mWidth; } UINT GetHeight() { return mHeight; } DXGI_FORMAT GetColorFormat() { return mColorFormat; } D3D11_MAPPED_SUBRESOURCE MapRenderTarget( CPUTRenderParameters ¶ms, eCPUTMapType type, bool wait=true ); void UnmapRenderTarget( CPUTRenderParameters ¶ms ); }; //-------------------------------------------------------------------------------------- class CPUTRenderTargetDepth { public: static ID3D11DepthStencilView *GetActiveDepthStencilView() { return spActiveDepthStencilView; } static void SetActiveDepthStencilView(ID3D11DepthStencilView *pView) { spActiveDepthStencilView = pView; } static void SetActiveWidthHeight( UINT width, UINT height ) {sCurrentWidth = width; sCurrentHeight=height; } static UINT GetActiveWidth() {return sCurrentWidth; } static UINT GetActiveHeight() {return sCurrentHeight; } protected: static UINT sCurrentWidth; static UINT sCurrentHeight; static ID3D11DepthStencilView *spActiveDepthStencilView; cString mName; UINT mWidth; UINT mHeight; bool mRenderTargetSet; DXGI_FORMAT mDepthFormat; UINT mMultiSampleCount; CD3D11_TEXTURE2D_DESC mDepthDesc; CPUTTexture *mpDepthTexture; ID3D11Texture2D *mpDepthTextureDX; ID3D11ShaderResourceView *mpDepthResourceView; ID3D11DepthStencilView *mpDepthStencilView; ID3D11Texture2D *mpDepthTextureDXStaging; eCPUTMapType mMappedType; UINT mSavedWidth; UINT mSavedHeight; ID3D11RenderTargetView *mpSavedColorRenderTargetView; ID3D11DepthStencilView *mpSavedDepthStencilView; public: CPUTRenderTargetDepth() : mWidth(0), mHeight(0), mRenderTargetSet(false), mDepthFormat(DXGI_FORMAT_UNKNOWN), mMultiSampleCount(1), mpDepthTexture(NULL), mpDepthTextureDX(NULL), mpDepthResourceView(NULL), mpDepthStencilView(NULL), mpDepthTextureDXStaging(NULL), mSavedWidth(0), mSavedHeight(0), mpSavedColorRenderTargetView(NULL), mpSavedDepthStencilView(NULL) { } ~CPUTRenderTargetDepth(); HRESULT CreateRenderTarget( cString textureName, UINT width, UINT height, DXGI_FORMAT depthFormat, UINT multiSampleCount = 1, bool recreate = false ); HRESULT RecreateRenderTarget( UINT width, UINT height, DXGI_FORMAT depthFormat = DXGI_FORMAT_UNKNOWN, UINT multiSampleCount = 1 ); void SetRenderTarget( CPUTRenderParameters &renderParams, DWORD renderTargetIndex = 0, float zClearVal = 0.0f, bool clear = false ); void RestoreRenderTarget( CPUTRenderParameters &renderParams ); ID3D11DepthStencilView *GetDepthBufferView() { return mpDepthStencilView; } ID3D11ShaderResourceView *GetDepthResourceView() { return mpDepthResourceView; } UINT GetWidth() { return mWidth; } UINT GetHeight() { return mHeight; } D3D11_MAPPED_SUBRESOURCE MapRenderTarget( CPUTRenderParameters ¶ms, eCPUTMapType type, bool wait=true ); void UnmapRenderTarget( CPUTRenderParameters ¶ms ); }; #endif // _CPUTRENDERTARGET_H ================================================ FILE: CPUT/CPUT/CPUTResource.h ================================================ //{{NO_DEPENDENCIES}} // Microsoft Visual C++ generated include file. // Used by test.rc // #define IDC_MYICON 2 #define IDD_TEST_DIALOG 102 #define IDS_APP_TITLE 103 #define IDD_ABOUTBOX 103 #define IDM_ABOUT 104 #define IDM_EXIT 105 #define IDI_TEST 107 #define IDI_SMALL 108 #define IDC_TEST 109 #define IDR_MAINFRAME 128 #define IDI_ICON1 129 #define IDC_STATIC -1 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NO_MFC 1 #define _APS_NEXT_RESOURCE_VALUE 130 #define _APS_NEXT_COMMAND_VALUE 32771 #define _APS_NEXT_CONTROL_VALUE 1000 #define _APS_NEXT_SYMED_VALUE 110 #endif #endif ================================================ FILE: CPUT/CPUT/CPUTShaderDX11.cpp ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #include "CPUTShaderDX11.h" #include "D3DCompiler.h" #include "CPUTConfigBlock.h" #include "CPUTMaterial.h" //----------------------------------------------------------------------------- bool CPUTShaderDX11::ShaderRequiresPerModelPayload( CPUTConfigBlock &properties ) { ID3D11ShaderReflection *pReflector = NULL; D3DReflect( mpBlob->GetBufferPointer(), mpBlob->GetBufferSize(), IID_ID3D11ShaderReflection, (void**)&pReflector); // Walk through the shader input bind descriptors. // If any of them begin with '@', then we need a unique material per model (i.e., we need to clone the material). int ii=0; D3D11_SHADER_INPUT_BIND_DESC desc; HRESULT hr = pReflector->GetResourceBindingDesc( ii++, &desc ); while( SUCCEEDED(hr) ) { cString tagName = s2ws(desc.Name); CPUTConfigEntry *pValue = properties.GetValueByName(tagName); if( !pValue->IsValid() ) { // We didn't find our property in the file. Is it in the global config block? pValue = CPUTMaterial::mGlobalProperties.GetValueByName(tagName); } cString boundName = pValue->ValueAsString(); if( (boundName.length() > 0) && ((boundName[0] == '@') || (boundName[0] == '#')) ) { return true; } hr = pReflector->GetResourceBindingDesc( ii++, &desc ); } return false; } ================================================ FILE: CPUT/CPUT/CPUTShaderDX11.h ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #ifndef _CPUTSHADERDX11_H #define _CPUTSHADERDX11_H #include "CPUT.h" #include "CPUTRefCount.h" class CPUTConfigBlock; class CPUTShaderDX11 : public CPUTRefCount { protected: ID3DBlob *mpBlob; // Destructor is not public. Must release instead of delete. ~CPUTShaderDX11(){ SAFE_RELEASE(mpBlob); } public: CPUTShaderDX11() : mpBlob(NULL) {} CPUTShaderDX11(ID3DBlob *pBlob) : mpBlob(pBlob) {} ID3DBlob *GetBlob() { return mpBlob; } bool ShaderRequiresPerModelPayload( CPUTConfigBlock &properties ); }; #endif //_CPUTPIXELSHADER_H ================================================ FILE: CPUT/CPUT/CPUTSlider.cpp ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #include "CPUTSlider.h" #include "CPUTText.h" #include // for fmod #include // static initializers CPUT_SIZE CPUTSlider::mpActiveImageSizeList[] = { {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, }; CPUT_SIZE CPUTSlider::mpPressedImageSizeList[] = { {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, }; CPUT_SIZE CPUTSlider::mpDisabledImageSizeList[] = { {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, }; // texture atlas information float gAtlasWidthSlider = 256.0f; float gAtlasHeightSlider= 64.0f; // Texture atlas coordinates of the active-idle button image int gUVLocationsSlider_active[] = { 141,0, 152,18, // neutral nub 170,5, 171,9, // tick 175,7, 178,11, // left tray end 183,7, 184,11, // tray 188,7, 190,11, // right tray end }; // Texture atlas coordinates of the pressed button image int gUVLocationsSlider_pressed[] = { 154,0, 165,18, // grabbed nub 170,5, 171,9, // tick 175,7, 178,11, // left tray end 183,7, 184,11, // tray 188,7, 190,11, // right tray end }; // Texture atlas coordinates of the disabled button image int gUVLocationsSlider_disabled[] = { 193,0, 204,18, // neutral nub 217,7, 218,11, // tick 206,7, 209,11, // left tray end 211,7, 212,11, // tray 214,7, 216,11, // right tray end }; float3 mpUVCoordsSlider_active[10]; float3 mpUVCoordsSlider_pressed[10]; float3 mpUVCoordsSlider_disabled[10]; const int Grip = 0; const int Tick = 1; const int TrayLeftCap = 2; const int Tray = 3; const int TrayRightCap = 4; const int NumQuadsinSlider = 5; // Constructor //------------------------------------------------------------------------------ CPUTSlider::CPUTSlider(const cString ControlText, CPUTControlID id, CPUTFont *pFont): mpControlText(NULL), mpMirrorBufferActive(NULL), mpMirrorBufferPressed(NULL), mpMirrorBufferDisabled(NULL), mSliderNubTickLocation(0), mSliderNubLocation(0.0f), mbMouseInside(false), mbStartedClickInside(false), mbDrawTicks(true), mpFont(pFont) { // initialize the state variables InitialStateSet(); // save the control ID for callbacks mcontrolID = id; // set as enabled CPUTControl::SetEnable(true); // default to an active control mControlState = CPUT_CONTROL_ACTIVE; // save the control ID mcontrolID = id; // set the string to display with the slider mpControlText = new CPUTText(mpFont); mpControlText->SetText(ControlText); // set default scale/start/end values SetScale(mSliderStartValue, mSliderEndValue, mSliderNumberOfSteps); } // Initial state of the control's member variables //------------------------------------------------------------------------------ void CPUTSlider::InitialStateSet() { mcontrolType = CPUT_SLIDER; mControlState = CPUT_CONTROL_ACTIVE; mSliderState = CPUT_SLIDER_NIB_UNPRESSED; mControlDimensions.x=0; mControlDimensions.y=0; mControlDimensions.width=0; mControlDimensions.height=0; // slider state/range mSliderStartValue = 1.0f; mSliderEndValue = 10.0f; mSliderNumberOfSteps = 10; mSliderNumberOfTicks = mSliderNumberOfSteps; mCurrentSliderValue = 1; } // destructor //------------------------------------------------------------------------------ CPUTSlider::~CPUTSlider() { UnRegisterInstanceResources(); } //CPUTSlider // Returns the number of quads needed to draw this control //-------------------------------------------------------------------------------- unsigned int CPUTSlider::GetOutputVertexCount() { // The base slider is made of: // 3 quads for the sliding 'tray' // 1 for the nub // 1 quad for each slider tick displayed // // ---+------------------+--- // | 0 | 1 | 2 | // ---+------------------+--- // Calculation: // 3 verticies/triangle * 2 triangles/quad * (3 + 1 + (1 * number of slider ticks) ) if(false == mbDrawTicks) { return (3 * 2) * (3 + 1); } return ( 3 * 2 ) * (3 + 1 + (1 * mSliderNumberOfTicks)); } // //-------------------------------------------------------------------------------- void CPUTSlider::SetPosition(int x, int y) { mControlDimensions.x = x; mControlDimensions.y = y; Recalculate(); // text is always in the upper-left corner of the control mpControlText->SetPosition(x,y); } // Register all resources shared by every slider control //-------------------------------------------------------------------------------- CPUTResult CPUTSlider::RegisterStaticResources() { // calculate the UV coordinates of each of the images that // make up the slider. Do this for the active, pressed, and disabled states. for(int ii=0; ii<10; ii++) { mpUVCoordsSlider_active[ii].x = gUVLocationsSlider_active[2*ii]/gAtlasWidthSlider; mpUVCoordsSlider_active[ii].y = gUVLocationsSlider_active[2*ii+1]/gAtlasHeightSlider; mpUVCoordsSlider_pressed[ii].x = gUVLocationsSlider_pressed[2*ii]/gAtlasWidthSlider; mpUVCoordsSlider_pressed[ii].y = gUVLocationsSlider_pressed[2*ii+1]/gAtlasHeightSlider; mpUVCoordsSlider_disabled[ii].x = gUVLocationsSlider_disabled[2*ii]/gAtlasWidthSlider; mpUVCoordsSlider_disabled[ii].y = gUVLocationsSlider_disabled[2*ii+1]/gAtlasHeightSlider; } // calculate the width/height in pixels of each of the 5 image slices // that makes up the slider int QuadIndex=0; for(int ii=0; ii ExtentX) { mSliderNubLocation = ExtentX; } // recalculate the location of the items Recalculate(); handledCode = CPUT_EVENT_HANDLED; return handledCode; } // did the slider just get released? if( (CPUT_SLIDER_NIB_PRESSED == mSliderState) && (CPUT_MOUSE_LEFT_DOWN != state) && (true == mbStartedClickInside )) { //mControlState = CPUT_CONTROL_ACTIVE; mSliderState = CPUT_SLIDER_NIB_UNPRESSED; SnapToNearestTick(); handledCode = CPUT_EVENT_HANDLED; // trigger the user's callback mpCallbackHandler->HandleCallbackEvent(1, mcontrolID, (CPUTControl*) this); // recalculate the location of the items Recalculate(); // reset flags mbStartedClickInside = false; return CPUT_EVENT_HANDLED; } // handle the initial grabbing of the nub if(ContainsPoint(x,y)) { handledCode = CPUT_EVENT_HANDLED; if(CPUT_MOUSE_LEFT_DOWN == state) { if(!PointingAtNub(x,y)) { mbMouseInside = false; mbStartedClickInside = false; //mControlState = CPUT_CONTROL_ACTIVE; mSliderState = CPUT_SLIDER_NIB_UNPRESSED; handledCode = CPUT_EVENT_UNHANDLED; } else { if(true == mbMouseInside) { mbStartedClickInside = true; //mControlState = CPUT_CONTROL_PRESSED; mSliderState = CPUT_SLIDER_NIB_PRESSED; } else { // ignore return CPUT_EVENT_UNHANDLED; } } } else { if(PointingAtNub(x,y)) { mbMouseInside = true; } else { //mControlState = CPUT_CONTROL_ACTIVE; mSliderState = CPUT_SLIDER_NIB_UNPRESSED; handledCode = CPUT_EVENT_UNHANDLED; mbStartedClickInside = false; mbMouseInside = false; } } } else { mbMouseInside = false; mbStartedClickInside = false; } return handledCode; } // Calculate important size info used by most of the system //-------------------------------------------------------------------------------- void CPUTSlider::CalculateLocationGuides(LocationGuides& guides) { CPUT_RECT rect; mpControlText->GetDimensions(rect.width, rect.height); guides.TickIndent = mpActiveImageSizeList[0].width / 2.0f; // cgrip guides.TextDownIndent = (float)(rect.height+2); guides.GripDownIndent = guides.TextDownIndent + mpActiveImageSizeList[1].height/2.0f; // CTick guides.TrayDownIndent = guides.TextDownIndent + (mpActiveImageSizeList[0].height / 2.0f) + mpActiveImageSizeList[1].height/2.0f; // CGrip CTick guides.TickSpacing = ( CPUT_DEFAULT_TRAY_WIDTH/(float)(mSliderNumberOfTicks-1)); guides.StepSpacing = ( CPUT_DEFAULT_TRAY_WIDTH/(float)(mSliderNumberOfSteps-1)); guides.TotalWidth = guides.TickIndent + mpActiveImageSizeList[2].width + CPUT_DEFAULT_TRAY_WIDTH + mpActiveImageSizeList[4].width; // CTrayLeftCap CTrayRightCap guides.TotalHeight = ( guides.TextDownIndent + guides.GripDownIndent + mpActiveImageSizeList[0].height); // CGrip } // return screen-space width/height of the control //-------------------------------------------------------------------------------- void CPUTSlider::GetDimensions(int &width, int &height) { LocationGuides guides; CalculateLocationGuides(guides); width=(int)guides.TotalWidth; height=(int)guides.TotalHeight; } // return the x/y window position of the control //-------------------------------------------------------------------------------- void CPUTSlider::GetPosition(int &x, int &y) { x = mControlDimensions.x; y = mControlDimensions.y; } // //-------------------------------------------------------------------------------- bool CPUTSlider::ContainsPoint(int x, int y) { // calculate all the locations we'll need for drawing LocationGuides guides; CalculateLocationGuides(guides); if( (x > ( mControlDimensions.x + guides.TotalWidth )) || (x < mControlDimensions.x) || (y < mControlDimensions.y) || (y > (mControlDimensions.y + guides.TotalHeight)) ) { return false; } return true; } // //-------------------------------------------------------------------------------- bool CPUTSlider::PointingAtNub(int x, int y) { // calculate all the locations we'll need for drawing LocationGuides guides; CalculateLocationGuides(guides); // locate the grabber coordinates float UpperLeftX=mSliderNubLocation+mControlDimensions.x; float UpperLeftY=(float)(mControlDimensions.y + guides.GripDownIndent); float LowerRightX = UpperLeftX + mpActiveImageSizeList[Grip].width; float LowerRightY = UpperLeftY + mpActiveImageSizeList[Grip].height; if( (x>LowerRightX) || (xLowerRightY ) ) { return false; } return true; } // //-------------------------------------------------------------------------------- void CPUTSlider::SnapToNearestTick() { LocationGuides guides; CalculateLocationGuides(guides); float locationOnTray = mSliderNubLocation; int index = (int) (locationOnTray/guides.StepSpacing); float remainder = locationOnTray - index*guides.StepSpacing; if(remainder > (guides.StepSpacing/2.0f)) { if(index<(mSliderNumberOfSteps-1)) { // snap to next higher spot index++; } // on really large scales (tiny step sizes) // you can actually get over by a pixel if(index>mSliderNumberOfSteps) { index = mSliderNumberOfSteps; } } // calculate the tick mark to align with mSliderNubLocation = (float)(guides.TickIndent + (index*guides.StepSpacing) - (mpActiveImageSizeList[Grip].width/2.0f)); Recalculate(); } // Once we get above a certain number of ticks - the ticks overlap and are // useless, look jenky, and //------------------------------------------------------------------------------ void CPUTSlider::ClampTicks() { // This number is somewhat arbitrary, but visually pleasing based on current // default GUI graphics. If you change the graphics, you might want to // change this number based on size of the tick, default slider size, etc if(mSliderNumberOfTicks > 100) { mSliderNumberOfTicks = 100; } } // Set the string that sits above the actual slider bar //------------------------------------------------------------------------------ void CPUTSlider::SetText(const cString ControlText) { mpControlText->SetText(ControlText); Recalculate(); // position or size may move - force a recalculation of this control's location // if it is managed by the auto-arrange function if(this->IsAutoArranged()) { mControlNeedsArrangmentResizing = true; } else { mControlGraphicsDirty = true; } } // Enable/disable the control //-------------------------------------------------------------------------------- void CPUTSlider::SetEnable(bool in_bEnabled) { if(in_bEnabled) { mControlState = CPUT_CONTROL_ACTIVE; } else { mControlState = CPUT_CONTROL_INACTIVE; } // set the control's text to match mpControlText->SetEnable(in_bEnabled); // recalculate control's quads Recalculate(); } // Enable the drawing of the ticks //------------------------------------------------------------------------------ void CPUTSlider::SetTickDrawing(bool DrawTicks) { mbDrawTicks = DrawTicks; Recalculate(); } //CPUTSlider // Set the Scale //-------------------------------------------------------------------------------- CPUTResult CPUTSlider::SetScale(float StartValue, float EndValue, int NumberOfSteps) { ASSERT( StartValue < EndValue, _L("Slider start greater or equal to slider end") ); ASSERT( NumberOfSteps > 1 , _L("Slider must have more than 2 steps from start to end value") ); mSliderStartValue = StartValue; mSliderEndValue = EndValue; mSliderNumberOfSteps = NumberOfSteps; mSliderNumberOfTicks = mSliderNumberOfSteps; // re-sizes the mirror buffers to accomidate the new items RegisterInstanceResources(); // to avoid the problem of changing the scale and having the gripper be // out of that range, setScale always sets the gripper to the start // value when re-ranging the control SetValue(StartValue); // Did we calculate a reasonable number of ticks? Or should we clamp them to a // visibly pleasing amount? ClampTicks(); // we've likely moved things, so re-calculate the vertex buffer Recalculate(); return CPUT_SUCCESS; } //-------------------------------------------------------------------------------- CPUTResult CPUTSlider::SetNumberOfTicks(int NumberOfTicks) { // you cannot have more ticks than there are steps if(mSliderNumberOfSteps < NumberOfTicks) { return CPUT_ERROR_INVALID_PARAMETER; } mSliderNumberOfTicks = NumberOfTicks; // Did we calculate a reasonable number of ticks? Or should we clamp them to a // visibly pleasing amount? ClampTicks(); // sometimes indent changes due to resetting, // so recalculate SnapToNearestTick(); return CPUT_SUCCESS; } // Get's the value the slider is current positioned at //-------------------------------------------------------------------------------- CPUTResult CPUTSlider::GetValue(float & fValue) { LocationGuides guides; CalculateLocationGuides(guides); float fractpart, intpart; fractpart = modf((mSliderNubLocation/guides.StepSpacing) , &intpart); int index = (int) intpart; if(fractpart>=0.5f) index++; float stepSizeValue = (mSliderEndValue - mSliderStartValue)/(float)(mSliderNumberOfSteps-1); // calculate the slider location's value fValue = mSliderStartValue + stepSizeValue*index; // In extreme range cases, the calculated value might be off by a fractional amount // prevent the slider from returning an above/below start/end value if(fValue > mSliderEndValue) { fValue = mSliderEndValue; } if(fValue < mSliderStartValue) { fValue = mSliderStartValue; } return CPUT_SUCCESS; } // Moves the slider gripper to the specified value on the slider //-------------------------------------------------------------------------------- CPUTResult CPUTSlider::SetValue(float fValue) { if(fValue>mSliderEndValue) { fValue = mSliderEndValue; } else if(fValue (guides.StepSpacing/2.0f)) { // snap to next higher spot index++; } float stepSizeValue = (mSliderEndValue - mSliderStartValue)/(mSliderNumberOfSteps-1); float remainderValue = (fValue-mSliderStartValue) - index*stepSizeValue; float remainderPercent = remainderValue/stepSizeValue; float remainderPixels = remainderPercent*guides.StepSpacing; mSliderNubLocation = (float)(guides.TickIndent + (index*guides.StepSpacing) + remainderPixels - (mpActiveImageSizeList[Grip].width/2.0f)); Recalculate(); return CPUT_SUCCESS; } // 'Draw' this control into the supplied vertex buffer object //-------------------------------------------------------------------------------- void CPUTSlider::DrawIntoBuffer(CPUTGUIVertex *pVertexBufferMirror, UINT *pInsertIndex, UINT pMaxBufferSize, CPUTGUIVertex *pTextVertexBufferMirror, UINT *pTextInsertIndex, UINT MaxTextVertexBufferSize) { // don't bother drawing if control is invisible, bad buffer pointers, etc if(!mControlVisible) { return; } if((NULL==pVertexBufferMirror) || (NULL==pInsertIndex)) { return; } if(!mpMirrorBufferActive || !mpMirrorBufferPressed) { return; } // Do we have enough room to put this control into the output buffer? int VertexCopyCount = GetOutputVertexCount(); ASSERT( (pMaxBufferSize >= *pInsertIndex + VertexCopyCount), _L("Too many CPUT GUI controls for allocated GUI buffer. Allocated GUI vertex buffer is too small.\n\nIncrease CPUT_GUI_BUFFER_SIZE size.") ); switch(mControlState) { case CPUT_CONTROL_ACTIVE: // copy the active+idle button into the stream if(CPUT_SLIDER_NIB_PRESSED == mSliderState) { memcpy(&pVertexBufferMirror[*pInsertIndex], mpMirrorBufferPressed, sizeof(CPUTGUIVertex)*VertexCopyCount); } else { memcpy(&pVertexBufferMirror[*pInsertIndex], mpMirrorBufferActive, sizeof(CPUTGUIVertex)*VertexCopyCount); } break; case CPUT_CONTROL_INACTIVE: // copy the inactive button into the stream memcpy(&pVertexBufferMirror[*pInsertIndex], mpMirrorBufferDisabled, sizeof(CPUTGUIVertex)*VertexCopyCount); break; default: // error! unknown state ASSERT(0,_L("CPUTCheckbox: Control is in unknown state")); return; } // move the uber-buffer index the tail of what we just added // each new quad has 6 verts in it (and each vert has 3+2 floats in it). *pInsertIndex+=VertexCopyCount; // now do the text // draw the text if(mpControlText) { mpControlText->DrawIntoBuffer((CPUTGUIVertex*) pTextVertexBufferMirror, pTextInsertIndex, MaxTextVertexBufferSize); } // we'll mark the control as no longer being 'dirty' mControlGraphicsDirty = false; } // This function re-calculates the positions of the various items in the control //------------------------------------------------------------------------ void CPUTSlider::Recalculate() { LocationGuides guides; CalculateLocationGuides(guides); // left tray cap float TrayX=(float)(mControlDimensions.x + guides.TickIndent/2.0f); float TrayY=(float)(mControlDimensions.y + guides.TrayDownIndent); AddQuadIntoMirrorBuffer(mpMirrorBufferActive, 0*6, (float) TrayX, (float) TrayY, (float) mpActiveImageSizeList[TrayLeftCap].width, (float) mpActiveImageSizeList[TrayLeftCap].height, mpUVCoordsSlider_active[TrayLeftCap*2+0], mpUVCoordsSlider_active[TrayLeftCap*2+1] ); AddQuadIntoMirrorBuffer(mpMirrorBufferPressed, 0*6, (float) TrayX, (float) TrayY, (float) mpActiveImageSizeList[TrayLeftCap].width, (float) mpActiveImageSizeList[TrayLeftCap].height, mpUVCoordsSlider_active[TrayLeftCap*2+0], mpUVCoordsSlider_active[TrayLeftCap*2+1] ); AddQuadIntoMirrorBuffer(mpMirrorBufferDisabled, 0*6, (float) TrayX, (float) TrayY, (float) mpDisabledImageSizeList[TrayLeftCap].width, (float) mpDisabledImageSizeList[TrayLeftCap].height, mpUVCoordsSlider_disabled[TrayLeftCap*2+0], mpUVCoordsSlider_disabled[TrayLeftCap*2+1] ); // tray TrayX=(float)(mControlDimensions.x + guides.TickIndent/2.0f + mpActiveImageSizeList[2].width); // +tray cap left TrayY=(float)(mControlDimensions.y + guides.TrayDownIndent); AddQuadIntoMirrorBuffer(mpMirrorBufferActive, 1*6, (float) TrayX, (float) TrayY, //(float) mpActiveImageSizeList[3].width+50, (CPUT_DEFAULT_TRAY_WIDTH+guides.TickIndent/2.0f), (float) mpActiveImageSizeList[Tray].height, mpUVCoordsSlider_active[Tray*2+0], mpUVCoordsSlider_active[Tray*2+1] ); AddQuadIntoMirrorBuffer(mpMirrorBufferPressed, 1*6, (float) TrayX, (float) TrayY, //(float) mpActiveImageSizeList[3].width+50, (CPUT_DEFAULT_TRAY_WIDTH+guides.TickIndent/2.0f), (float) mpActiveImageSizeList[Tray].height, mpUVCoordsSlider_active[Tray*2+0], mpUVCoordsSlider_active[Tray*2+1] ); AddQuadIntoMirrorBuffer(mpMirrorBufferDisabled, 1*6, (float) TrayX, (float) TrayY, //(float) mpActiveImageSizeList[3].width+50, (CPUT_DEFAULT_TRAY_WIDTH+guides.TickIndent/2.0f), (float) mpDisabledImageSizeList[Tray].height, mpUVCoordsSlider_disabled[Tray*2+0], mpUVCoordsSlider_disabled[Tray*2+1] ); // right tray cap TrayX=(float)(mControlDimensions.x + guides.TickIndent/2.0f + mpActiveImageSizeList[3].width + CPUT_DEFAULT_TRAY_WIDTH + guides.TickIndent/2.0f + 1.0); TrayY=(float)(mControlDimensions.y + guides.TrayDownIndent); AddQuadIntoMirrorBuffer(mpMirrorBufferActive, 2*6, (float) TrayX, (float) TrayY, (float) mpActiveImageSizeList[TrayRightCap].width, (float) mpActiveImageSizeList[TrayRightCap].height, mpUVCoordsSlider_active[TrayRightCap*2+0], mpUVCoordsSlider_active[TrayRightCap*2+1] ); AddQuadIntoMirrorBuffer(mpMirrorBufferPressed, 2*6, (float) TrayX, (float) TrayY, (float) mpActiveImageSizeList[TrayRightCap].width, (float) mpActiveImageSizeList[TrayRightCap].height, mpUVCoordsSlider_active[TrayRightCap*2+0], mpUVCoordsSlider_active[TrayRightCap*2+1] ); AddQuadIntoMirrorBuffer(mpMirrorBufferDisabled, 2*6, (float) TrayX, (float) TrayY, (float) mpDisabledImageSizeList[TrayRightCap].width, (float) mpDisabledImageSizeList[TrayRightCap].height, mpUVCoordsSlider_disabled[TrayRightCap*2+0], mpUVCoordsSlider_disabled[TrayRightCap*2+1] ); // nub TrayX = mControlDimensions.x + mSliderNubLocation; TrayY=(float)(mControlDimensions.y + guides.GripDownIndent); AddQuadIntoMirrorBuffer(mpMirrorBufferActive, 3*6, (float) TrayX, (float) TrayY, (float) mpActiveImageSizeList[Grip].width, (float) mpActiveImageSizeList[Grip].height, mpUVCoordsSlider_active[Grip*2+0], mpUVCoordsSlider_active[Grip*2+1] ); AddQuadIntoMirrorBuffer(mpMirrorBufferPressed, 3*6, (float) TrayX, (float) TrayY, (float) mpPressedImageSizeList[Grip].width, (float) mpPressedImageSizeList[Grip].height, mpUVCoordsSlider_pressed[Grip*2+0], mpUVCoordsSlider_pressed[Grip*2+1] ); AddQuadIntoMirrorBuffer(mpMirrorBufferDisabled, 3*6, (float) TrayX, (float) TrayY, (float) mpDisabledImageSizeList[Grip].width, (float) mpDisabledImageSizeList[Grip].height, mpUVCoordsSlider_disabled[Grip*2+0], mpUVCoordsSlider_disabled[Grip*2+1] ); if(mbDrawTicks) { // ticks int uberBufferIndex=4; for(int i=0; iGetMaterial( spriteMaterialName, false ); // Define the input layout D3D11_INPUT_ELEMENT_DESC pLayout[] = { { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }, { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 }, {0} }; CPUTVertexShaderDX11 *pVertexShader = ((CPUTMaterialDX11*)mpMaterial)->GetVertexShader(); ID3D11Device *pD3dDevice = CPUT_DX11::GetDevice(); CPUTInputLayoutCacheDX11::GetInputLayoutCache()->GetLayout( pD3dDevice, pLayout, pVertexShader, &mpInputLayout); // *************************************************** // Create Vertex Buffers // *************************************************** D3D11_BUFFER_DESC bd; bd.Usage = D3D11_USAGE_DYNAMIC; bd.ByteWidth = sizeof(SpriteVertex) * 6; // 2 tris, 3 verts each vertices bd.BindFlags = D3D11_BIND_VERTEX_BUFFER; bd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; bd.MiscFlags = 0; const float top = -spriteY; //-1.0f; const float bottom = -spriteY - spriteHeight; // 1.0f; const float left = spriteX; //-1.0f; const float right = spriteX + spriteWidth; // 1.0f; SpriteVertex pVertices[] = { { left, top, 1.0f, 0.0f, 0.0f }, { right, top, 1.0f, 1.0f, 0.0f }, { left, bottom, 1.0f, 0.0f, 1.0f }, { right, top, 1.0f, 1.0f, 0.0f }, { right, bottom, 1.0f, 1.0f, 1.0f }, { left, bottom, 1.0f, 0.0f, 1.0f } }; D3D11_SUBRESOURCE_DATA initialData; initialData.pSysMem = pVertices; initialData.SysMemPitch = sizeof( SpriteVertex ); initialData.SysMemSlicePitch = 0; result = pD3dDevice->CreateBuffer( &bd, &initialData, &mpVertexBuffer ); ASSERT( SUCCEEDED(result), _L("Failed creating render target debug-sprite vertex buffer") ); CPUTSetDebugName( mpVertexBuffer, _L("CPUTSprite vertex buffer") ); return S_OK; } // CPUTSprite::CreateSprite() //----------------------------------------- void CPUTSprite::DrawSprite( CPUTRenderParameters &renderParams, CPUTMaterial &material ) { // TODO: Should we warn here? // If it doesn't draw, make sure you created it with createDebugSprite == true if( mpVertexBuffer ) { ID3D11DeviceContext *pContext = ((CPUTRenderParametersDX*)&renderParams)->mpContext; material.SetRenderStates(renderParams); UINT stride = sizeof( SpriteVertex ); UINT offset = 0; pContext->IASetVertexBuffers( 0, 1, &mpVertexBuffer, &stride, &offset ); // Set the input layout pContext->IASetInputLayout( mpInputLayout ); // Set primitive topology pContext->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST ); pContext->Draw( 6, 0 ); } } // CPUTSprite::DrawSprite() ================================================ FILE: CPUT/CPUT/CPUTSprite.h ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #ifndef _CPUTSPRITE_H #define _CPUTSPRITE_H #include "CPUT.h" #include "d3d11.h" class CPUTMaterial; class CPUTRenderParameters; class CPUTTexture; class CPUTSprite { protected: class SpriteVertex { public: float mpPos[3]; float mpUV[2]; }; ID3D11Buffer *mpVertexBuffer; CPUTMaterial *mpMaterial; ID3D11InputLayout *mpInputLayout; public: CPUTSprite() : mpInputLayout(NULL), mpVertexBuffer(NULL), mpMaterial(NULL) { } ~CPUTSprite(); HRESULT CreateSprite( float spriteX = -1.0f, float spriteY = -1.0f, float spriteWidth = 2.0f, float spriteHeight = 2.0f, const cString &materialName = cString(_L("Sprite")) ); void DrawSprite( CPUTRenderParameters &renderParams ) { DrawSprite( renderParams, *mpMaterial ); } void DrawSprite( CPUTRenderParameters &renderParams, CPUTMaterial &material ); }; #endif // _CPUTSPRITE_H ================================================ FILE: CPUT/CPUT/CPUTText.cpp ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #include "CPUTText.h" #include "CPUTFont.h" #include // texture atlas information float gTextAtlasWidth = 750.0f; float gTextAtlasHeight = 12.0f; // Constructor //------------------------------------------------------------------------------ CPUTText::CPUTText(CPUTFont *pFont):mVertexStride(0), mVertexOffset(0), mpMirrorBuffer(NULL), mNumCharsInString(0), mZDepth(1.0f), mpFont(pFont) { // initialize the state variables InitialStateSet(); mQuadSize.height=0; mQuadSize.width=0; mPosition.x=0; mPosition.y=0; mStaticText.clear(); } // Constructor //----------------------------------------------------------------------------- CPUTText::CPUTText(const cString String, CPUTControlID id, CPUTFont *pFont):mVertexStride(0), mVertexOffset(0), mpMirrorBuffer(NULL), mNumCharsInString(0), mZDepth(1.0f), mpFont(pFont) { // initialize the state variables InitialStateSet(); // save the control ID for callbacks mcontrolID = id; // set as enabled CPUTControl::SetEnable(true); // reset position/sizes mQuadSize.height=0; mQuadSize.width=0; mPosition.x=0; mPosition.y=0; // set the text SetText(String); // store the control id mcontrolID = id; } // Initial state of the control's member variables //----------------------------------------------------------------------------- void CPUTText::InitialStateSet() { mcontrolType = CPUT_STATIC; mStaticState = CPUT_CONTROL_ACTIVE; mPosition.x=0; mPosition.y=0; mQuadSize.width=0; mQuadSize.height=0; mDimensions.x=0; mDimensions.y=0; mDimensions.width=0; mDimensions.height=0; // set default text color mColor.r=1.0; mColor.g=1.0; mColor.b=1.0; mColor.a=1.0; } // Destructor //------------------------------------------------------------------------------ CPUTText::~CPUTText() { ReleaseInstanceData(); } // Return the dimensions of this static text object (in pixels) //-------------------------------------------------------------------------------- void CPUTText::GetDimensions(int &width, int &height) { width = mQuadSize.width; height = mQuadSize.height; } // Return the screen position of this static text object (in pixels) //-------------------------------------------------------------------------------- void CPUTText::GetPosition(int &x, int &y) { x = mPosition.x; y = mPosition.y; } // fills user defined buffer with static string //-------------------------------------------------------------------------------- void CPUTText::GetString(cString &ButtonText) { // fill user defined buffer with the string ButtonText = mStaticText; } // Enable/disable the text //-------------------------------------------------------------------------------- void CPUTText::SetEnable(bool in_bEnabled) { // set as enabled CPUTControl::SetEnable(in_bEnabled); // recalculate Recalculate(); // position or size may move - force a recalculation of this control's location // if it is managed by the auto-arrange function if(this->IsAutoArranged()) { mControlNeedsArrangmentResizing = true; } } // Release all instance data //-------------------------------------------------------------------------------- void CPUTText::ReleaseInstanceData() { SAFE_DELETE_ARRAY(mpMirrorBuffer); } //-------------------------------------------------------------------------------- CPUTResult CPUTText::RegisterInstanceData() { CPUTResult result = CPUT_SUCCESS; // ping the font system and tell it to load this font - or add GetFont() to the AssetLibrary(!?) // get the pertinent mapping data back for that font // calculate the uv's/etc for each character // hold onto a retrievable copy of the texture text atlas that can be returned with this object/or make a function that sets that map (material?) return result; } // Register all static assets (used by all CPUTText objects) //-------------------------------------------------------------------------------- CPUTResult CPUTText::RegisterStaticResources() { return CPUT_SUCCESS; } // //-------------------------------------------------------------------------------- CPUTResult CPUTText::UnRegisterStaticResources() { // todo: Release font? //CPUTFontLibraryDX11::GetFontLibrary()->DeleteFont( mFontID ); return CPUT_SUCCESS; } // //-------------------------------------------------------------------------------- void CPUTText::SetPosition(int x, int y) { mPosition.x = x; mPosition.y = y; Recalculate(); } //-------------------------------------------------------------------------------- void CPUTText::DrawIntoBuffer(CPUTGUIVertex *pVertexBufferMirror, UINT *pInsertIndex, UINT pMaxBufferSize) { if(!mControlVisible) { return; } if((NULL==pVertexBufferMirror) || (NULL==pInsertIndex)) { return; } if(!mpMirrorBuffer) // || !mpMirrorBufferDisabled) { return; } // Do we have enough room to put the text vertexes into the output buffer? int VertexCopyCount = GetOutputVertexCount(); ASSERT( (pMaxBufferSize >= *pInsertIndex + VertexCopyCount), _L("Too many characters to fit in allocated GUI string vertex buffer.\n\nIncrease CPUT_GUI_BUFFER_STRING_SIZE size.") ); // copy the string quads into the target buffer memcpy(&pVertexBufferMirror[*pInsertIndex], mpMirrorBuffer, sizeof(CPUTGUIVertex)*6*mNumCharsInString); *pInsertIndex+=6*mNumCharsInString; // we'll mark the control as no longer being 'dirty' mControlGraphicsDirty = false; } // Calculate the number of verticies will be needed to display this string // //-------------------------------------------------------------------------------- int CPUTText::GetOutputVertexCount() { // A string is made of one quad per character (including spaces) // // --- // | 1 | // --- // // calculation: (number of characters in string) * 3 verticies/triangle * 2 triangles/quad * 1 quad/character return mNumCharsInString * (2*3); } // using the supplied font, build up a quad for each character in the string // and assemble the quads into a vertex buffer ready for drawing/memcpy //-------------------------------------------------------------------------------- void CPUTText::Recalculate() { SAFE_DELETE_ARRAY(mpMirrorBuffer); mNumCharsInString = (int) mStaticText.size(); mpMirrorBuffer = new CPUTGUIVertex[(mNumCharsInString+1)*6]; bool Enabled = false; if(CPUT_CONTROL_ACTIVE == mControlState) { Enabled = true; } // walk each character and build a quad from it float characterPosition=0; for(int ii=0; iiGetGlyphSize(character); mpFont->GetGlyphUVS(character, Enabled, UV1, UV2); if('\t'!=character) { AddQuadIntoMirrorBuffer(mpMirrorBuffer, ii*6, (float)mPosition.x+characterPosition, (float)mPosition.y, (float)size.width, (float)size.height, UV1, UV2); } else { // calculate tab width = # pixels to get to next tabstop // Tabs are relative from BEGINNNING of the string, not absolute based on x-position of string. So in order for columns to line // up, you need the starts of the strings to line up too. // If you want 'absolute' x alignment behavior, use this: //size.width = size.width - ((mPosition.x+characterPosition) % size.width); // simply skip the amount of space indicated in the font's tab slot int CurrentPositionForNextGlyph = (int) characterPosition; size.width = size.width - (CurrentPositionForNextGlyph % size.width); AddQuadIntoMirrorBuffer(mpMirrorBuffer, ii*6, (float)mPosition.x+characterPosition, (float)mPosition.y, (float)size.width, (float)size.height, UV1, UV2); } // store the max height of the string mQuadSize.height = max(mQuadSize.height, size.height); // step to next X location for next character characterPosition+=size.width; } // store the total width of the string mQuadSize.width = (int)characterPosition; // width of string in pixels // tell gui system this control image is now dirty // and needs to rebuild it's draw list mControlGraphicsDirty = true; // position or size may move - force a recalculation of this control's location // if it is managed by the auto-arrange function if(this->IsAutoArranged()) { mControlNeedsArrangmentResizing = true; } } // Register quad for drawing string on //-------------------------------------------------------------------------------- CPUTResult CPUTText::SetText(const cString String, float depth) { HEAPCHECK; mStaticText = String; mZDepth = depth; // call recalculate function to generate new quad // list to display this text Recalculate(); HEAPCHECK; return CPUT_SUCCESS; } // This generates a quad with the supplied coordinates/uv's/etc. //------------------------------------------------------------------------ void CPUTText::AddQuadIntoMirrorBuffer(CPUTGUIVertex *pMirrorBuffer, int index, float x, float y, float w, float h, float3 uv1, float3 uv2 ) { pMirrorBuffer[index+0].Pos = float3( x + 0.0f, y + 0.0f, mZDepth); pMirrorBuffer[index+0].UV = float2(uv1.x, uv1.y); pMirrorBuffer[index+0].Color = mColor; pMirrorBuffer[index+1].Pos = float3( x + w, y + 0.0f, mZDepth); pMirrorBuffer[index+1].UV = float2(uv2.x, uv1.y); pMirrorBuffer[index+1].Color = mColor; pMirrorBuffer[index+2].Pos = float3( x + 0.0f, y + h, mZDepth); pMirrorBuffer[index+2].UV = float2(uv1.x, uv2.y); pMirrorBuffer[index+2].Color = mColor; pMirrorBuffer[index+3].Pos = float3( x + w, y + 0.0f, mZDepth); pMirrorBuffer[index+3].UV = float2(uv2.x, uv1.y); pMirrorBuffer[index+3].Color = mColor; pMirrorBuffer[index+4].Pos = float3( x + w, y + h, mZDepth); pMirrorBuffer[index+4].UV = float2(uv2.x, uv2.y); pMirrorBuffer[index+4].Color = mColor; pMirrorBuffer[index+5].Pos = float3( x + 0.0f, y +h, mZDepth); pMirrorBuffer[index+5].UV = float2(uv1.x, uv2.y); pMirrorBuffer[index+5].Color = mColor; } //------------------------------------------------------------------------ CPUTResult CPUTText::SetColor(float r, float g, float b, float a) { CPUTColor4 color; color.r = r; color.g = g; color.b = b; color.a = a; return SetColor(color); } //------------------------------------------------------------------------ CPUTResult CPUTText::SetColor(CPUTColor4 color) { if(color != mColor) { mColor = color; // dirty Recalculate(); } return CPUT_SUCCESS; } //------------------------------------------------------------------------ CPUTColor4 CPUTText::GetColor() { return mColor; } ================================================ FILE: CPUT/CPUT/CPUTText.h ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #ifndef __CPUTTEXT_H__ #define __CPUTTEXT_H__ #include "CPUTControl.h" #include "CPUTGuiController.h" class CPUTFont; // Button base - common functionality for all the controls //----------------------------------------------------------------------------- class CPUTText:public CPUTControl { public: // button should self-register with the GuiController on create CPUTText(CPUTFont *pFont); CPUTText(CPUTText& copy); CPUTText(const cString String, CPUTControlID id, CPUTFont *pFont); virtual ~CPUTText(); //Management virtual void GetString(cString &ButtonText); //CPUTEventHandler virtual CPUTEventHandledCode HandleKeyboardEvent(CPUTKey key){UNREFERENCED_PARAMETER(key); return CPUT_EVENT_UNHANDLED;} virtual CPUTEventHandledCode HandleMouseEvent(int x, int y, int wheel, CPUTMouseState state){UNREFERENCED_PARAMETER(x);UNREFERENCED_PARAMETER(y);UNREFERENCED_PARAMETER(wheel);UNREFERENCED_PARAMETER(state);return CPUT_EVENT_UNHANDLED;} //CPUTControl void GetDimensions(int &width, int &height); void GetPosition(int &x, int &y); // CPUTControl virtual void SetPosition(int x, int y); void SetEnable(bool in_bEnabled); bool ContainsPoint(int x, int y) {UNREFERENCED_PARAMETER(x);UNREFERENCED_PARAMETER(y);return false;} // CPUTText CPUTResult SetText(const cString String, float depth=0.5f); CPUTResult SetColor(float r, float g, float b, float a); CPUTResult SetColor(CPUTColor4 color); CPUTColor4 GetColor(); int GetOutputVertexCount(); // Register assets CPUTResult RegisterInstanceData(); static CPUTResult RegisterStaticResources(); static CPUTResult UnRegisterStaticResources(); // draw void DrawIntoBuffer(CPUTGUIVertex *pVertexBufferMirror, UINT *pInsertIndex, UINT pMaxBufferSize); protected: // instance variables CPUT_POINT mPosition; CPUT_RECT mDimensions; CPUT_SIZE mQuadSize; CPUTGUIControlState mStaticState; CPUTColor4 mColor; UINT mVertexStride; UINT mVertexOffset; CPUTFont *mpFont; // uber-buffer cString mStaticText; float mZDepth; CPUTGUIVertex *mpMirrorBuffer; int mNumCharsInString; static CPUT_SIZE mpStaticIdleImageSizeList[500]; // todo: size for #chars in font static CPUT_SIZE mpStaticDisabledImageSizeList[500]; // todo: size for #chars in font // helper functions void InitialStateSet(); void ReleaseInstanceData(); void Recalculate(); void AddQuadIntoMirrorBuffer(CPUTGUIVertex *pMirrorBuffer, int index, float x, float y, float w, float h, float3 uv1, float3 uv2 ); }; #endif //#ifndef __CPUTTEXT_H__ ================================================ FILE: CPUT/CPUT/CPUTTexture.cpp ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #include "CPUTTexture.h" #ifdef CPUT_FOR_DX11 #include "CPUTTextureDX11.h" #else #error You must supply a target graphics API (ex: #define CPUT_FOR_DX11), or implement the target API for this file. #endif CPUTTexture *CPUTTexture::CreateTexture( const cString &name, const cString absolutePathAndFilename, bool loadAsSRGB ) { // TODO: accept DX11/OGL param to control which platform we generate. // TODO: be sure to support the case where we want to support only one of them #ifdef CPUT_FOR_DX11 return CPUTTextureDX11::CreateTexture( name, absolutePathAndFilename, loadAsSRGB ); #else #error You must supply a target graphics API (ex: #define CPUT_FOR_DX11), or implement the target API for this file. #endif } ================================================ FILE: CPUT/CPUT/CPUTTexture.h ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #ifndef _CPUTTEXTURE_H #define _CPUTTEXTURE_H #include "CPUT.h" #include "CPUTRefCount.h" class CPUTTexture : public CPUTRefCount { protected: cString mName; eCPUTMapType mMappedType; ~CPUTTexture(){} // Destructor is not public. Must release instead of delete. public: CPUTTexture() : mMappedType(CPUT_MAP_UNDEFINED) {} CPUTTexture(cString &name) : mMappedType(CPUT_MAP_UNDEFINED), mName(name) {} static CPUTTexture *CreateTexture( const cString &name, const cString absolutePathAndFilename, bool loadAsSRGB ); virtual D3D11_MAPPED_SUBRESOURCE MapTexture( CPUTRenderParameters ¶ms, eCPUTMapType type, bool wait=true ) = 0; virtual void UnmapTexture( CPUTRenderParameters ¶ms ) =0; // TODO: Store params on Map() and don't require here. }; #endif //_CPUTTEXTURE_H ================================================ FILE: CPUT/CPUT/CPUTTextureDX11.cpp ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #include "CPUTTextureDX11.h" // TODO: Would be nice to find a better place for this decl. But, not another file just for this. const cString gDXGIFormatNames[] = { _L("DXGI_FORMAT_UNKNOWN "), _L("DXGI_FORMAT_R32G32B32A32_TYPELESS"), _L("DXGI_FORMAT_R32G32B32A32_FLOAT"), _L("DXGI_FORMAT_R32G32B32A32_UINT"), _L("DXGI_FORMAT_R32G32B32A32_SINT"), _L("DXGI_FORMAT_R32G32B32_TYPELESS"), _L("DXGI_FORMAT_R32G32B32_FLOAT"), _L("DXGI_FORMAT_R32G32B32_UINT"), _L("DXGI_FORMAT_R32G32B32_SINT"), _L("DXGI_FORMAT_R16G16B16A16_TYPELESS"), _L("DXGI_FORMAT_R16G16B16A16_FLOAT"), _L("DXGI_FORMAT_R16G16B16A16_UNORM"), _L("DXGI_FORMAT_R16G16B16A16_UINT"), _L("DXGI_FORMAT_R16G16B16A16_SNORM"), _L("DXGI_FORMAT_R16G16B16A16_SINT"), _L("DXGI_FORMAT_R32G32_TYPELESS"), _L("DXGI_FORMAT_R32G32_FLOAT"), _L("DXGI_FORMAT_R32G32_UINT"), _L("DXGI_FORMAT_R32G32_SINT"), _L("DXGI_FORMAT_R32G8X24_TYPELESS"), _L("DXGI_FORMAT_D32_FLOAT_S8X24_UINT"), _L("DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS"), _L("DXGI_FORMAT_X32_TYPELESS_G8X24_UINT"), _L("DXGI_FORMAT_R10G10B10A2_TYPELESS"), _L("DXGI_FORMAT_R10G10B10A2_UNORM"), _L("DXGI_FORMAT_R10G10B10A2_UINT"), _L("DXGI_FORMAT_R11G11B10_FLOAT"), _L("DXGI_FORMAT_R8G8B8A8_TYPELESS"), _L("DXGI_FORMAT_R8G8B8A8_UNORM"), _L("DXGI_FORMAT_R8G8B8A8_UNORM_SRGB"), _L("DXGI_FORMAT_R8G8B8A8_UINT"), _L("DXGI_FORMAT_R8G8B8A8_SNORM"), _L("DXGI_FORMAT_R8G8B8A8_SINT"), _L("DXGI_FORMAT_R16G16_TYPELESS"), _L("DXGI_FORMAT_R16G16_FLOAT"), _L("DXGI_FORMAT_R16G16_UNORM"), _L("DXGI_FORMAT_R16G16_UINT"), _L("DXGI_FORMAT_R16G16_SNORM"), _L("DXGI_FORMAT_R16G16_SINT"), _L("DXGI_FORMAT_R32_TYPELESS"), _L("DXGI_FORMAT_D32_FLOAT"), _L("DXGI_FORMAT_R32_FLOAT"), _L("DXGI_FORMAT_R32_UINT"), _L("DXGI_FORMAT_R32_SINT"), _L("DXGI_FORMAT_R24G8_TYPELESS"), _L("DXGI_FORMAT_D24_UNORM_S8_UINT"), _L("DXGI_FORMAT_R24_UNORM_X8_TYPELESS"), _L("DXGI_FORMAT_X24_TYPELESS_G8_UINT"), _L("DXGI_FORMAT_R8G8_TYPELESS"), _L("DXGI_FORMAT_R8G8_UNORM"), _L("DXGI_FORMAT_R8G8_UINT"), _L("DXGI_FORMAT_R8G8_SNORM"), _L("DXGI_FORMAT_R8G8_SINT"), _L("DXGI_FORMAT_R16_TYPELESS"), _L("DXGI_FORMAT_R16_FLOAT"), _L("DXGI_FORMAT_D16_UNORM"), _L("DXGI_FORMAT_R16_UNORM"), _L("DXGI_FORMAT_R16_UINT"), _L("DXGI_FORMAT_R16_SNORM"), _L("DXGI_FORMAT_R16_SINT"), _L("DXGI_FORMAT_R8_TYPELESS"), _L("DXGI_FORMAT_R8_UNORM"), _L("DXGI_FORMAT_R8_UINT"), _L("DXGI_FORMAT_R8_SNORM"), _L("DXGI_FORMAT_R8_SINT"), _L("DXGI_FORMAT_A8_UNORM"), _L("DXGI_FORMAT_R1_UNORM"), _L("DXGI_FORMAT_R9G9B9E5_SHAREDEXP"), _L("DXGI_FORMAT_R8G8_B8G8_UNORM"), _L("DXGI_FORMAT_G8R8_G8B8_UNORM"), _L("DXGI_FORMAT_BC1_TYPELESS"), _L("DXGI_FORMAT_BC1_UNORM"), _L("DXGI_FORMAT_BC1_UNORM_SRGB"), _L("DXGI_FORMAT_BC2_TYPELESS"), _L("DXGI_FORMAT_BC2_UNORM"), _L("DXGI_FORMAT_BC2_UNORM_SRGB"), _L("DXGI_FORMAT_BC3_TYPELESS"), _L("DXGI_FORMAT_BC3_UNORM"), _L("DXGI_FORMAT_BC3_UNORM_SRGB"), _L("DXGI_FORMAT_BC4_TYPELESS"), _L("DXGI_FORMAT_BC4_UNORM"), _L("DXGI_FORMAT_BC4_SNORM"), _L("DXGI_FORMAT_BC5_TYPELESS"), _L("DXGI_FORMAT_BC5_UNORM"), _L("DXGI_FORMAT_BC5_SNORM"), _L("DXGI_FORMAT_B5G6R5_UNORM"), _L("DXGI_FORMAT_B5G5R5A1_UNORM"), _L("DXGI_FORMAT_B8G8R8A8_UNORM"), _L("DXGI_FORMAT_B8G8R8X8_UNORM"), _L("DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM"), _L("DXGI_FORMAT_B8G8R8A8_TYPELESS"), _L("DXGI_FORMAT_B8G8R8A8_UNORM_SRGB"), _L("DXGI_FORMAT_B8G8R8X8_TYPELESS"), _L("DXGI_FORMAT_B8G8R8X8_UNORM_SRGB"), _L("DXGI_FORMAT_BC6H_TYPELESS"), _L("DXGI_FORMAT_BC6H_UF16"), _L("DXGI_FORMAT_BC6H_SF16"), _L("DXGI_FORMAT_BC7_TYPELESS"), _L("DXGI_FORMAT_BC7_UNORM"), _L("DXGI_FORMAT_BC7_UNORM_SRGB") }; const cString *gpDXGIFormatNames = gDXGIFormatNames; //----------------------------------------------------------------------------- CPUTTexture *CPUTTextureDX11::CreateTexture( const cString &name, const cString &absolutePathAndFilename, bool loadAsSRGB ) { // TODO: Delegate to derived class. We don't currently have CPUTTextureDX11 ID3D11ShaderResourceView *pShaderResourceView = NULL; ID3D11Resource *pTexture = NULL; ID3D11Device *pD3dDevice= CPUT_DX11::GetDevice(); CPUTResult result = CreateNativeTexture( pD3dDevice, absolutePathAndFilename, &pShaderResourceView, &pTexture, loadAsSRGB ); ASSERT( CPUTSUCCESS(result), _L("Error loading texture: '")+absolutePathAndFilename ); CPUTTextureDX11 *pNewTexture = new CPUTTextureDX11(); pNewTexture->mName = name; pNewTexture->SetTextureAndShaderResourceView( pTexture, pShaderResourceView ); pTexture->Release(); pShaderResourceView->Release(); CPUTAssetLibrary::GetAssetLibrary()->AddTexture( absolutePathAndFilename, pNewTexture); return pNewTexture; } //----------------------------------------------------------------------------- CPUTResult CPUTTextureDX11::CreateNativeTexture( ID3D11Device *pD3dDevice, const cString &fileName, ID3D11ShaderResourceView **ppShaderResourceView, ID3D11Resource **ppTexture, bool ForceLoadAsSRGB ){ CPUTResult result; HRESULT hr; // Set up loading structure // // Indicate all texture parameters should come from the file D3DX11_IMAGE_LOAD_INFO LoadInfo; ZeroMemory(&LoadInfo, sizeof(D3DX11_IMAGE_LOAD_INFO)); LoadInfo.Width = D3DX11_FROM_FILE; LoadInfo.Height = D3DX11_FROM_FILE; LoadInfo.Depth = D3DX11_FROM_FILE; LoadInfo.FirstMipLevel = D3DX11_FROM_FILE; LoadInfo.MipLevels = D3DX11_FROM_FILE; // LoadInfo.Usage = D3D11_USAGE_IMMUTABLE; // TODO: maintain a "mappable" flag? Set immutable if not mappable? LoadInfo.Usage = D3D11_USAGE_DEFAULT; LoadInfo.BindFlags = D3D11_BIND_SHADER_RESOURCE; LoadInfo.CpuAccessFlags = 0; LoadInfo.MiscFlags = 0; LoadInfo.MipFilter = D3DX11_FROM_FILE; LoadInfo.pSrcInfo = NULL; LoadInfo.Format = (DXGI_FORMAT) D3DX11_FROM_FILE; LoadInfo.Filter = D3DX11_FILTER_NONE; // if we're 'forcing' load of sRGB data, we need to verify image is sRGB // or determine image format that best matches the non-sRGB source format in hopes that the conversion will be faster // and data preserved if(true == ForceLoadAsSRGB) { // get the source image info D3DX11_IMAGE_INFO SrcInfo; hr = D3DX11GetImageInfoFromFile(fileName.c_str(), NULL, &SrcInfo, NULL); ASSERT( SUCCEEDED(hr), _L(" - Error loading texture '")+fileName+_L("'.") ); // find a closest equivalent sRGB format result = GetSRGBEquivalent(SrcInfo.Format, LoadInfo.Format); ASSERT( CPUTSUCCESS(result), _L("Error loading texture '")+fileName+_L("'. It is specified this texture must load as sRGB, but the source image is in a format that cannot be converted to sRGB.\n") ); // set filtering mode to interpret 'in'-coming data as sRGB, and storing it 'out' on an sRGB surface // // As it stands, we don't have any tools that support sRGB output in DXT compressed textures. // If we later support a format that does provide sRGB, then the filter 'in' flag will need to be removed LoadInfo.Filter = D3DX11_FILTER_NONE | D3DX11_FILTER_SRGB_IN | D3DX11_FILTER_SRGB_OUT; #if 0 // DWM: TODO: We want to catch the cases where the loader needs to do work. // This happens if the texture's pixel format isn't supported by DXGI. // TODO: how to determine? // if a runtime conversion must happen report a performance warning error. // Note: choosing not to assert here, as this will be a common issue. if( SrcInfo.Format != LoadInfo.Format) { cString dxgiName = GetDXGIFormatString(SrcInfo.Format); cString errorString = _T(__FUNCTION__); errorString += _L("- PERFORMANCE WARNING: '") + fileName +_L("' has an image format ")+dxgiName +_L(" but must be run-time converted to ")+GetDXGIFormatString(LoadInfo.Format) +_L(" based on requested sRGB target buffer.\n"); TRACE( errorString.c_str() ); } #endif } hr = D3DX11CreateTextureFromFile( pD3dDevice, fileName.c_str(), &LoadInfo, NULL, ppTexture, NULL ); ASSERT( SUCCEEDED(hr), _L("Failed to load texture: ") + fileName ); CPUTSetDebugName( *ppTexture, fileName ); hr = pD3dDevice->CreateShaderResourceView( *ppTexture, NULL, ppShaderResourceView ); ASSERT( SUCCEEDED(hr), _L("Failed to create texture shader resource view.") ); CPUTSetDebugName( *ppShaderResourceView, fileName ); return CPUT_SUCCESS; } //----------------------------------------------------------------------------- CPUTResult CPUTTextureDX11::GetSRGBEquivalent(DXGI_FORMAT inFormat, DXGI_FORMAT& sRGBFormat) { switch( inFormat ) { case DXGI_FORMAT_R8G8B8A8_UNORM: case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: sRGBFormat = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB; return CPUT_SUCCESS; case DXGI_FORMAT_B8G8R8X8_UNORM: case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB: sRGBFormat = DXGI_FORMAT_B8G8R8X8_UNORM_SRGB; return CPUT_SUCCESS; case DXGI_FORMAT_BC1_UNORM: case DXGI_FORMAT_BC1_UNORM_SRGB: sRGBFormat = DXGI_FORMAT_BC1_UNORM_SRGB; return CPUT_SUCCESS; case DXGI_FORMAT_BC2_UNORM: case DXGI_FORMAT_BC2_UNORM_SRGB: sRGBFormat = DXGI_FORMAT_BC2_UNORM_SRGB; return CPUT_SUCCESS; case DXGI_FORMAT_BC3_UNORM: case DXGI_FORMAT_BC3_UNORM_SRGB: sRGBFormat = DXGI_FORMAT_BC3_UNORM_SRGB; return CPUT_SUCCESS; case DXGI_FORMAT_BC7_UNORM: case DXGI_FORMAT_BC7_UNORM_SRGB: sRGBFormat = DXGI_FORMAT_BC7_UNORM_SRGB; return CPUT_SUCCESS; }; return CPUT_ERROR_UNSUPPORTED_SRGB_IMAGE_FORMAT; } // This function returns the DXGI string equivalent of the DXGI format for // error reporting/display purposes //----------------------------------------------------------------------------- const cString &CPUTTextureDX11::GetDXGIFormatString(DXGI_FORMAT format) { ASSERT( (format>=0) && (format<=DXGI_FORMAT_BC7_UNORM_SRGB), _L("Invalid DXGI Format.") ); return gpDXGIFormatNames[format]; } // Given a certain DXGI texture format, does it even have an equivalent sRGB one //----------------------------------------------------------------------------- bool CPUTTextureDX11::DoesExistEquivalentSRGBFormat(DXGI_FORMAT inFormat) { DXGI_FORMAT outFormat; if( CPUT_ERROR_UNSUPPORTED_SRGB_IMAGE_FORMAT == GetSRGBEquivalent(inFormat, outFormat) ) { return false; } return true; } //----------------------------------------------------------------------------- D3D11_MAPPED_SUBRESOURCE CPUTTextureDX11::MapTexture( CPUTRenderParameters ¶ms, eCPUTMapType type, bool wait ) { // Mapping for DISCARD requires dynamic buffer. Create dynamic copy? // Could easily provide input flag. But, where would we specify? Don't like specifying in the .set file // Because mapping is something the application wants to do - it isn't inherent in the data. // Could do Clone() and pass dynamic flag to that. // But, then we have two. Could always delete the other. // Could support programatic flag - apply to all loaded models in the .set // Could support programatic flag on model. Load model first, then load set. // For now, simply support CopyResource mechanism. HRESULT hr; ID3D11Device *pD3dDevice = CPUT_DX11::GetDevice(); CPUTRenderParametersDX *pParamsDX11 = (CPUTRenderParametersDX*)¶ms; ID3D11DeviceContext *pContext = pParamsDX11->mpContext; if( !mpTextureStaging ) { // Annoying. We need to create the texture differently, based on dimension. D3D11_RESOURCE_DIMENSION dimension; mpTexture->GetType(&dimension); switch( dimension ) { case D3D11_RESOURCE_DIMENSION_TEXTURE1D: { D3D11_TEXTURE1D_DESC desc; ((ID3D11Texture1D*)mpTexture)->GetDesc( &desc ); desc.Usage = D3D11_USAGE_STAGING; switch( type ) { case CPUT_MAP_READ: desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; desc.BindFlags = 0; break; case CPUT_MAP_READ_WRITE: desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE; desc.BindFlags = 0; break; case CPUT_MAP_WRITE: case CPUT_MAP_WRITE_DISCARD: case CPUT_MAP_NO_OVERWRITE: desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; desc.BindFlags = 0; break; }; hr = pD3dDevice->CreateTexture1D( &desc, NULL, (ID3D11Texture1D**)&mpTextureStaging ); ASSERT( SUCCEEDED(hr), _L("Failed to create staging texture") ); break; } case D3D11_RESOURCE_DIMENSION_TEXTURE2D: { D3D11_TEXTURE2D_DESC desc; ((ID3D11Texture2D*)mpTexture)->GetDesc( &desc ); desc.Usage = D3D11_USAGE_STAGING; switch( type ) { case CPUT_MAP_READ: desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; desc.BindFlags = 0; break; case CPUT_MAP_READ_WRITE: desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE; desc.BindFlags = 0; break; case CPUT_MAP_WRITE: case CPUT_MAP_WRITE_DISCARD: case CPUT_MAP_NO_OVERWRITE: desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; desc.BindFlags = 0; break; }; hr = pD3dDevice->CreateTexture2D( &desc, NULL, (ID3D11Texture2D**)&mpTextureStaging ); ASSERT( SUCCEEDED(hr), _L("Failed to create staging texture") ); break; } case D3D11_RESOURCE_DIMENSION_TEXTURE3D: { D3D11_TEXTURE3D_DESC desc; ((ID3D11Texture3D*)mpTexture)->GetDesc( &desc ); desc.Usage = D3D11_USAGE_STAGING; switch( type ) { case CPUT_MAP_READ: desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; desc.BindFlags = 0; break; case CPUT_MAP_READ_WRITE: desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE; desc.BindFlags = 0; break; case CPUT_MAP_WRITE: case CPUT_MAP_WRITE_DISCARD: case CPUT_MAP_NO_OVERWRITE: desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; desc.BindFlags = 0; break; }; hr = pD3dDevice->CreateTexture3D( &desc, NULL, (ID3D11Texture3D**)&mpTextureStaging ); ASSERT( SUCCEEDED(hr), _L("Failed to create staging texture") ); break; } default: ASSERT(0, _L("Unkown texture dimension") ); break; } } else { ASSERT( mMappedType == type, _L("Mapping with a different CPU access than creation parameter.") ); } D3D11_MAPPED_SUBRESOURCE info; switch( type ) { case CPUT_MAP_READ: case CPUT_MAP_READ_WRITE: // TODO: Copying and immediately mapping probably introduces a stall. // Expose the copy externally? // TODO: copy only if changed? // Copy only first time? // Copy the GPU version before we read from it. pContext->CopyResource( mpTextureStaging, mpTexture ); break; }; hr = pContext->Map( mpTextureStaging, wait ? 0 : D3D11_MAP_FLAG_DO_NOT_WAIT, (D3D11_MAP)type, 0, &info ); mMappedType = type; return info; } // CPUTTextureDX11::Map() //----------------------------------------------------------------------------- void CPUTTextureDX11::UnmapTexture( CPUTRenderParameters ¶ms ) { ASSERT( mMappedType != CPUT_MAP_UNDEFINED, _L("Can't unmap a render target that isn't mapped.") ); CPUTRenderParametersDX *pParamsDX11 = (CPUTRenderParametersDX*)¶ms; ID3D11DeviceContext *pContext = pParamsDX11->mpContext; pContext->Unmap( mpTextureStaging, 0 ); // If we were mapped for write, then copy staging buffer to GPU switch( mMappedType ) { case CPUT_MAP_READ: break; case CPUT_MAP_READ_WRITE: case CPUT_MAP_WRITE: case CPUT_MAP_WRITE_DISCARD: case CPUT_MAP_NO_OVERWRITE: pContext->CopyResource( mpTexture, mpTextureStaging ); break; }; } // CPUTTextureDX11::Unmap() ================================================ FILE: CPUT/CPUT/CPUTTextureDX11.h ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #ifndef _CPUTTEXTUREDX11_H #define _CPUTTEXTUREDX11_H #include "CPUTTexture.h" #include "CPUT_DX11.h" #include #include class CPUTTextureDX11 : public CPUTTexture { private: // resource view pointer CD3D11_TEXTURE2D_DESC mDesc; ID3D11ShaderResourceView *mpShaderResourceView; ID3D11Resource *mpTexture; ID3D11Resource *mpTextureStaging; // Destructor is not public. Must release instead of delete. ~CPUTTextureDX11() { SAFE_RELEASE( mpShaderResourceView ); SAFE_RELEASE( mpTexture ); SAFE_RELEASE( mpTextureStaging ); } public: static const cString &GetDXGIFormatString(DXGI_FORMAT Format); static CPUTResult GetSRGBEquivalent(DXGI_FORMAT inFormat, DXGI_FORMAT& sRGBFormat); static bool DoesExistEquivalentSRGBFormat(DXGI_FORMAT inFormat); static CPUTTexture *CreateTexture( const cString &name, const cString &absolutePathAndFilename, bool loadAsSRGB ); static CPUTResult CreateNativeTexture( ID3D11Device *pD3dDevice, const cString &fileName, ID3D11ShaderResourceView **ppShaderResourceView, ID3D11Resource **ppTexture, bool forceLoadAsSRGB ); CPUTTextureDX11() : mpShaderResourceView(NULL), mpTexture(NULL), mpTextureStaging(NULL) {} CPUTTextureDX11(cString &name) : mpShaderResourceView(NULL), mpTexture(NULL), mpTextureStaging(NULL), CPUTTexture(name) {} CPUTTextureDX11(cString &name, ID3D11Resource *pTextureResource, ID3D11ShaderResourceView *pSrv ) : mpTextureStaging(NULL), CPUTTexture(name) { mpShaderResourceView = pSrv; if(mpShaderResourceView) pSrv->AddRef(); mpTexture = pTextureResource; if(mpTexture) mpTexture->AddRef(); } void ReleaseTexture() { SAFE_RELEASE(mpShaderResourceView); SAFE_RELEASE(mpTexture); } void SetTexture(ID3D11Resource *pTextureResource, ID3D11ShaderResourceView *pSrv ) { mpShaderResourceView = pSrv; if(mpShaderResourceView) pSrv->AddRef(); mpTexture = pTextureResource; if(mpTexture) mpTexture->AddRef(); } ID3D11ShaderResourceView* GetShaderResourceView() { return mpShaderResourceView; } void SetTextureAndShaderResourceView(ID3D11Resource *pTexture, ID3D11ShaderResourceView *pShaderResourceView) { // release any resources we might already be pointing too SAFE_RELEASE( mpTexture ); SAFE_RELEASE( mpTextureStaging ); // Now out-of sync. Will be recreated on next Map(). SAFE_RELEASE( mpShaderResourceView ); mpTexture = pTexture; if( mpTexture ) mpTexture->AddRef(); mpShaderResourceView = pShaderResourceView; mpShaderResourceView->AddRef(); } D3D11_MAPPED_SUBRESOURCE MapTexture( CPUTRenderParameters ¶ms, eCPUTMapType type, bool wait=true ); void UnmapTexture( CPUTRenderParameters ¶ms ); }; #endif //_CPUTTEXTUREDX11_H ================================================ FILE: CPUT/CPUT/CPUTTimer.h ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #ifndef CPUTTIMER_H #define CPUTTIMER_H class CPUTTimer { public: virtual void StartTimer() = 0; virtual double StopTimer() = 0; virtual double GetTotalTime() = 0; // In seconds virtual double GetElapsedTime() = 0; // In seconds virtual void ResetTimer() = 0; virtual ~CPUTTimer() {}; }; #endif CPUTTIMER_H ================================================ FILE: CPUT/CPUT/CPUTTimerWin.cpp ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #include "CPUTTimerWin.h" // // Timer is initially not running and set to a zero state. // Performance counter frequency is obtained. // CPUTTimerWin::CPUTTimerWin():mbCounting(false) { ResetTimer(); // // Frequency only needs to be collected once. // QueryPerformanceFrequency(&mlFrequency); } // // Reset timer to zero // void CPUTTimerWin::ResetTimer() { mlStartCount.QuadPart = 0; mlLastMeasured.QuadPart = 0; mbCounting = false; } // // Starts timer and resets everything. If timer is already running, // nothing happens. // void CPUTTimerWin::StartTimer() { if(!mbCounting) { ResetTimer(); mbCounting = true; QueryPerformanceCounter(&mlLastMeasured); mlStartCount = mlLastMeasured; } } // // If the timer is running, stops the timer and returns the time in seconds since StartTimer() was called. // // If the timer is not running, returns the time in seconds between the // last start/stop pair. // double CPUTTimerWin::StopTimer() { if(mbCounting) { mbCounting = false; QueryPerformanceCounter(&mlLastMeasured); } return (((double)(mlLastMeasured.QuadPart - mlStartCount.QuadPart)) / ((double)(mlFrequency.QuadPart))); } // // If the timer is running, returns the total time in seconds since StartTimer was called. // // If the timer is not running, returns the time in seconds between the // last start/stop pair. // double CPUTTimerWin::GetTotalTime() { LARGE_INTEGER temp; if (mbCounting) { QueryPerformanceCounter(&temp); return ((double)(temp.QuadPart - mlStartCount.QuadPart) / (double)(mlFrequency.QuadPart)); } return ((double)(mlLastMeasured.QuadPart - mlStartCount.QuadPart) / (double)(mlFrequency.QuadPart)); } // // If the timer is running, returns the total time in seconds that the timer // has run since it was last started or elapsed time was read from. Updates last measured time. // // If the timer is not running, returns 0. // double CPUTTimerWin::GetElapsedTime() { LARGE_INTEGER temp; LARGE_INTEGER elapsedTime; elapsedTime.QuadPart = 0; if (mbCounting) { QueryPerformanceCounter(&temp); elapsedTime.QuadPart = temp.QuadPart - mlLastMeasured.QuadPart; mlLastMeasured = temp; } return ((double)elapsedTime.QuadPart / (double)mlFrequency.QuadPart); } ================================================ FILE: CPUT/CPUT/CPUTTimerWin.h ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #ifndef __CPUTTIMER_H__ #define __CPUTTIMER_H__ #include "CPUT.h" #include "Windows.h" #include "CPUTTimer.h" // Simple QueryPerformanceCounter()-based timer class //----------------------------------------------------------------------------- class CPUTTimerWin : CPUTTimer { public: CPUTTimerWin(); virtual void StartTimer(); virtual double StopTimer(); virtual double GetTotalTime(); virtual double GetElapsedTime(); virtual void ResetTimer(); private: bool mbCounting; LARGE_INTEGER mlStartCount; LARGE_INTEGER mlLastMeasured; LARGE_INTEGER mlFrequency; }; #endif // #ifndef __CPUTTIMER_H__ ================================================ FILE: CPUT/CPUT/CPUTVertexShaderDX11.cpp ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #include "CPUTVertexShaderDX11.h" #include "CPUTAssetLibraryDX11.h" CPUTVertexShaderDX11 *CPUTVertexShaderDX11::CreateVertexShader( const cString &name, ID3D11Device *pD3dDevice, const cString &shaderMain, const cString &shaderProfile ) { ID3DBlob *pCompiledBlob = NULL; ID3D11VertexShader *pNewVertexShader = NULL; CPUTAssetLibraryDX11 *pAssetLibrary = (CPUTAssetLibraryDX11*)CPUTAssetLibrary::GetAssetLibrary(); CPUTResult result = pAssetLibrary->CompileShaderFromFile(name, shaderMain, shaderProfile, &pCompiledBlob); ASSERT( CPUTSUCCESS(result), _L("Error compiling vertex shader:\n\n") ); // Create the vertex shader // TODO: Move to vertex shader class HRESULT hr = pD3dDevice->CreateVertexShader( pCompiledBlob->GetBufferPointer(), pCompiledBlob->GetBufferSize(), NULL, &pNewVertexShader ); ASSERT( SUCCEEDED(hr), _L("Error creating vertex shader:\n\n") ); // cString DebugName = _L("CPUTAssetLibraryDX11::GetVertexShader ")+name; // CPUTSetDebugName(pNewVertexShader, DebugName); CPUTVertexShaderDX11 *pNewCPUTVertexShader = new CPUTVertexShaderDX11( pNewVertexShader, pCompiledBlob ); // add shader to library pAssetLibrary->AddVertexShader(name + shaderMain + shaderProfile, pNewCPUTVertexShader); // pNewCPUTVertexShader->Release(); // We've added it to the library, so release our reference // return the shader (and blob) return pNewCPUTVertexShader; } //-------------------------------------------------------------------------------------- CPUTVertexShaderDX11 *CPUTVertexShaderDX11::CreateVertexShaderFromMemory( const cString &name, ID3D11Device *pD3dDevice, const cString &shaderMain, const cString &shaderProfile, const char *pShaderSource ) { ID3DBlob *pCompiledBlob = NULL; ID3D11VertexShader *pNewVertexShader = NULL; CPUTAssetLibraryDX11 *pAssetLibrary = (CPUTAssetLibraryDX11*)CPUTAssetLibrary::GetAssetLibrary(); CPUTResult result = pAssetLibrary->CompileShaderFromMemory(pShaderSource, shaderMain, shaderProfile, &pCompiledBlob); ASSERT( CPUTSUCCESS(result), _L("Error compiling vertex shader:\n\n") ); // Create the vertex shader // TODO: Move to vertex shader class HRESULT hr = pD3dDevice->CreateVertexShader( pCompiledBlob->GetBufferPointer(), pCompiledBlob->GetBufferSize(), NULL, &pNewVertexShader ); ASSERT( SUCCEEDED(hr), _L("Error creating vertex shader:\n\n") ); // cString DebugName = _L("CPUTAssetLibraryDX11::GetVertexShader ")+name; // CPUTSetDebugName(pNewVertexShader, DebugName); CPUTVertexShaderDX11 *pNewCPUTVertexShader = new CPUTVertexShaderDX11( pNewVertexShader, pCompiledBlob ); // add shader to library pAssetLibrary->AddVertexShader(name + shaderMain + shaderProfile, pNewCPUTVertexShader); // pNewCPUTVertexShader->Release(); // We've added it to the library, so release our reference // return the shader (and blob) return pNewCPUTVertexShader; } ================================================ FILE: CPUT/CPUT/CPUTVertexShaderDX11.h ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #ifndef _CPUTVERTEXSHADERDX11_H #define _CPUTVERTEXSHADERDX11_H #include "CPUT.h" #include "CPUTShaderDX11.h" class CPUTVertexShaderDX11 : public CPUTShaderDX11 { protected: ID3D11VertexShader *mpVertexShader; // Destructor is not public. Must release instead of delete. ~CPUTVertexShaderDX11(){ SAFE_RELEASE(mpVertexShader) } public: static CPUTVertexShaderDX11 *CreateVertexShader( const cString &name, ID3D11Device *pD3dDevice, const cString &shaderMain, const cString &shaderProfile ); static CPUTVertexShaderDX11 *CreateVertexShaderFromMemory( const cString &name, ID3D11Device *pD3dDevice, const cString &shaderMain, const cString &shaderProfile, const char *pShaderSource ); CPUTVertexShaderDX11() : mpVertexShader(NULL), CPUTShaderDX11(NULL) {} CPUTVertexShaderDX11(ID3D11VertexShader *pD3D11VertexShader, ID3DBlob *pBlob) : mpVertexShader(pD3D11VertexShader), CPUTShaderDX11(pBlob) {} ID3D11VertexShader *GetNativeVertexShader() { return mpVertexShader; } }; #endif //_CPUTVERTEXSHADER_H ================================================ FILE: CPUT/CPUT/CPUTWindowWin.cpp ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #include "CPUTWindowWin.h" #ifdef _DEBUG TCHAR lpMsgBuf[100]; // declare global in case error is about lack of resources _inline void HandleWin32Error() { DWORD err = GetLastError(); FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), lpMsgBuf, 100, NULL ); ASSERT(false, lpMsgBuf); } #else _inline void HandleWin32Error() {} #endif // static initializers CPUT* CPUTWindowWin::mCPUT=NULL; bool CPUTWindowWin::mbMaxMinFullScreen=false; // Constructor //----------------------------------------------------------------------------- CPUTWindowWin::CPUTWindowWin():mhInst(0), mhWnd(0), mAppClosedReturnCode(0) { mAppTitle.clear(); } // Destructor //----------------------------------------------------------------------------- CPUTWindowWin::~CPUTWindowWin() { mAppTitle.clear(); } // Create window //----------------------------------------------------------------------------- CPUTResult CPUTWindowWin::Create(CPUT* cput, const cString WindowTitle, const int windowWidth, const int windowHeight, int windowX, int windowY) { if(mhWnd) { return CPUT_ERROR_WINDOW_ALREADY_EXISTS; } ASSERT( (windowX < GetSystemMetrics(SM_CXFULLSCREEN) && (windowX>=-1)), _L("You are attempting to create a window outside the desktop coordinates. Check your CPUTWindowCreationParams")); ASSERT( (windowY < GetSystemMetrics(SM_CYFULLSCREEN) && (windowY>=-1)), _L("You are attempting to create a window outside the desktop coordinates. Check your CPUTWindowCreationParams")); // get the hInstance of this executable mhInst = GetModuleHandle(NULL); if(NULL==mhInst) { return CPUT_ERROR_CANNOT_GET_WINDOW_INSTANCE; } // set up app title (if not specified) mAppTitle = WindowTitle; if(0==mAppTitle.compare(_L(""))) { mAppTitle = _L("CPUT Sample"); } // Register the Win32 class for this app WNDCLASS wc; if(TRUE == GetClassInfo(mhInst, mAppTitle.c_str(), &wc)) { // point to the existing one mhInst = wc.hInstance; } else { // register a new windows class ATOM classID; classID = MyRegisterClass(mhInst); if(0==classID) { HandleWin32Error(); return CPUT_ERROR_WINDOW_CANNOT_REGISTER_APP; } } // Perform Win32 instance initialization const int nCmdShow = SW_SHOWNORMAL; if (false == InitInstance(nCmdShow, windowWidth, windowHeight, windowX, windowY)) { return CPUT_ERROR_CANNOT_GET_WINDOW_INSTANCE; } // store the CPUT pointer mCPUT = (CPUT*) cput; return CPUT_SUCCESS; } // Destroy window //----------------------------------------------------------------------------- int CPUTWindowWin::Destroy() { DestroyWindow(mhWnd); mCPUT = NULL; return true; } // Window return code on close //----------------------------------------------------------------------------- int CPUTWindowWin::ReturnCode() { return mAppClosedReturnCode; } // Register window class //----------------------------------------------------------------------------- ATOM CPUTWindowWin::MyRegisterClass(HINSTANCE hInstance) { WNDCLASSEX wcex; wcex.cbSize = sizeof(WNDCLASSEX); // load icon from resource file LPCTSTR iconPathName= L"CPUT.ico"; UINT icon_flags = LR_LOADFROMFILE | LR_DEFAULTSIZE; HANDLE hIcon = LoadImage(hInstance, iconPathName, IMAGE_ICON, 0, 0, icon_flags); // set up RegisterClass struct wcex.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = WndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; wcex.hIcon = (HICON) hIcon; //LoadIcon(hInstance, iconSTR); wcex.hCursor = LoadCursor(NULL, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); wcex.lpszMenuName = NULL; wcex.lpszClassName = mAppTitle.c_str(); wcex.hIconSm = NULL; // LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL)); // no small icon for now // register the window class return RegisterClassEx(&wcex); } // InitInstance // Saves the windows instance handle, creates, and displays the main program // window //----------------------------------------------------------------------------- BOOL CPUTWindowWin::InitInstance(int nCmdShow, int windowWidth, int windowHeight, int windowX, int windowY) { // assure we have a valid hInstance ASSERT(NULL!=mhInst, _L("")); // zero sized windows means - you choose the size. :) if( (0==windowWidth) || (0==windowHeight) ) { CPUTOSServices *pServices = CPUTOSServices::GetOSServices(); pServices->GetDesktopDimensions(&windowWidth, &windowHeight); // default window size 1280x720 // but if screen is smaller than 1280x720, then pick 1/3 the screen size // so that it doesn't appear off the edges if(1280>windowWidth) { windowWidth = (2*windowWidth)/3; windowHeight = (2*windowHeight)/3; } else { windowWidth=1280; windowHeight=720; } } // set up size structure RECT rc = { 0, 0, windowWidth, windowHeight }; AdjustWindowRect( &rc, WS_OVERLAPPEDWINDOW, FALSE ); // if x = -1, then let windows decide where to put it if(-1==windowX) { windowX = CW_USEDEFAULT; } // create the window mhWnd = CreateWindow(mAppTitle.c_str(), mAppTitle.c_str(), WS_OVERLAPPEDWINDOW, windowX, //CW_USEDEFAULT, windowY, //CW_USEDEFAULT, rc.right - rc.left, rc.bottom - rc.top, NULL, NULL, mhInst, NULL); if (!mhWnd) { return FALSE; } ShowWindow(mhWnd, nCmdShow); UpdateWindow(mhWnd); // initialize the OS services with the hWND so you can make // reference to this object CPUTOSServices *pServices = CPUTOSServices::GetOSServices(); pServices->SethWnd(mhWnd); return TRUE; } // // WndProc // Handles the main message loop's events/messages //----------------------------------------------------------------------------- LRESULT CALLBACK CPUTWindowWin::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { CPUTEventHandledCode handledCode = CPUT_EVENT_UNHANDLED; LRESULT res; switch (message) { case WM_COMMAND: int wmId, wmEvent; wmId = LOWORD(wParam); wmEvent = HIWORD(wParam); // handle any menu item events here // see reference code in file history for examples break; case WM_KEYDOWN: if(mCPUT) { CPUTKey key = ConvertSpecialKeyCode(wParam, lParam); if(KEY_NONE!=key) { handledCode = mCPUT->CPUTHandleKeyboardEvent( key ); } } break; case WM_CHAR: // WM_KEYDOWN: gives you EVERY key - including shifts/etc if(mCPUT) { CPUTKey key = ConvertKeyCode(wParam, lParam); if(KEY_NONE!=key) { handledCode = mCPUT->CPUTHandleKeyboardEvent( key ); } } break; case WM_LBUTTONDBLCLK: case WM_MBUTTONDBLCLK: case WM_RBUTTONDBLCLK: // handle double-click events break; case WM_LBUTTONDOWN: case WM_LBUTTONUP: case WM_MBUTTONDOWN: case WM_MBUTTONUP: case WM_RBUTTONDOWN: case WM_RBUTTONUP: case WM_MOUSEMOVE: if(mCPUT) { CPUTMouseState state = ConvertMouseState(wParam); short xPos = LOWORD(lParam); short yPos = HIWORD(lParam); handledCode = mCPUT->CPUTHandleMouseEvent(xPos, yPos, 0, state); } break; case WM_MOUSEWHEEL: if(mCPUT) { // get mouse position short xPos = LOWORD(lParam); short yPos = HIWORD(lParam); // get wheel delta int wheel = GET_WHEEL_DELTA_WPARAM(wParam); // one 'click' handledCode = mCPUT->CPUTHandleMouseEvent(xPos, yPos, wheel, CPUT_MOUSE_WHEEL); } return 0; break; case WM_PAINT: PAINTSTRUCT ps; HDC hdc; hdc = BeginPaint(hWnd, &ps); EndPaint(hWnd, &ps); break; case WM_SIZING: case WM_MOVING: case WM_ERASEBKGND: // overriding this to do nothing avoids flicker and // the expense of re-creating tons of gfx contexts as it resizes break; //case WM_ACTIVATE: // check for maximize/minimize // break; case WM_SIZE: int width, height; height = HIWORD(lParam); width = LOWORD(lParam); RECT windowRect; if(0==GetClientRect(hWnd, &windowRect)) // this gets the client area inside the window frame *excluding* frames/menu bar/etc break; width = windowRect.right - windowRect.left; height = windowRect.bottom - windowRect.top; // if we have shrunk to 0 width/height - do not pass on this kind of resize - leads to // various render target resizing warnings if(0==width || 0==height) break; if(mCPUT) { // maximize/minimize effect if( (SIZE_MAXIMIZED == wParam) ) { // resize for new max/min size mCPUT->ResizeWindow(width,height); mbMaxMinFullScreen = true; } else if(SIZE_RESTORED == wParam) { if(true == mbMaxMinFullScreen) { // resize for new max/min size mCPUT->ResizeWindow(width,height); mbMaxMinFullScreen = false; } else { // do a stretch-blit while actively sizing by just rendering to un-resized back buffer mCPUT->ResizeWindowSoft(width, height); } } } break; case WM_EXITSIZEMOVE: // update the system's size and make callback if(mCPUT) { RECT windowRect; if(0==GetClientRect(hWnd, &windowRect)) // this gets the client area inside the window frame *excluding* frames/menu bar/etc break; width = windowRect.right - windowRect.left; height = windowRect.bottom - windowRect.top; // if we have shrunk to 0 width/height - do not pass on this kind of resize - leads to // various render target resizing warnings if(0==width || 0==height) break; mCPUT->ResizeWindow(width,height); } break; case WM_DESTROY: // time to shut down the system PostQuitMessage(0); break; default: // we don't handle it - pass it on thru to parent res = DefWindowProc(hWnd, message, wParam, lParam); return res; } // translate handled code if(CPUT_EVENT_HANDLED == handledCode) { return 1; } return 0; } // Convert OS specific key events to CPUT events //-----------------------------------------------------------------------------da CPUTKey CPUTWindowWin::ConvertKeyCode(WPARAM wParam, LPARAM lParam) { UNREFERENCED_PARAMETER(wParam); UNREFERENCED_PARAMETER(lParam); switch(wParam) { case 'a': case 'A': return KEY_A; case 'b': case 'B': return KEY_B; case 'c': case 'C': return KEY_C; case 'd': case 'D': return KEY_D; case 'e': case 'E': return KEY_E; case 'f': case 'F': return KEY_F; case 'g': case 'G': return KEY_G; case 'h': case 'H': return KEY_H; case 'i': case 'I': return KEY_I; case 'j': case 'J': return KEY_J; case 'k': case 'K': return KEY_K; case 'l': case 'L': return KEY_L; case 'm': case 'M': return KEY_M; case 'n': case 'N': return KEY_N; case 'o': case 'O': return KEY_O; case 'p': case 'P': return KEY_P; case 'Q': case 'q': return KEY_Q; case 'r': case 'R': return KEY_R; case 's': case 'S': return KEY_S; case 't': case 'T': return KEY_T; case 'u': case 'U': return KEY_U; case 'v': case 'V': return KEY_V; case 'w': case 'W': return KEY_W; case 'x': case 'X': return KEY_X; case 'y': case 'Y': return KEY_Y; case 'z': case 'Z': return KEY_Z; // number keys case '1': return KEY_1; case '2': return KEY_2; case '3': return KEY_3; case '4': return KEY_4; case '5': return KEY_5; case '6': return KEY_6; case '7': return KEY_7; case '8': return KEY_8; case '9': return KEY_9; case '0': return KEY_0; // symbols case ' ': return KEY_SPACE; case '`': return KEY_BACKQUOTE; case '~': return KEY_TILDE; case '!': return KEY_EXCLAMATION; case '@': return KEY_AT; case '#': return KEY_HASH; case '$': return KEY_$; case '%': return KEY_PERCENT; case '^': return KEY_CARROT; case '&': return KEY_ANDSIGN; case '*': return KEY_STAR; case '(': return KEY_OPENPAREN; case ')': return KEY_CLOSEPARN; case '_': return KEY__; case '-': return KEY_MINUS; case '+': return KEY_PLUS; case '[': return KEY_OPENBRACKET; case ']': return KEY_CLOSEBRACKET; case '{': return KEY_OPENBRACE; case '}': return KEY_CLOSEBRACE; case '\\': return KEY_BACKSLASH; case '|': return KEY_PIPE; case ';': return KEY_SEMICOLON; case ':': return KEY_COLON; case '\'': return KEY_SINGLEQUOTE; case '\"': return KEY_QUOTE; case ',': return KEY_COMMA; case '.': return KEY_PERIOD; case '/': return KEY_SLASH; case '<': return KEY_LESS; case '>': return KEY_GREATER; case '?': return KEY_QUESTION; } return KEY_NONE; } // Convert extended key events to CPUT events //----------------------------------------------------------------------------- CPUTKey CPUTWindowWin::ConvertSpecialKeyCode(WPARAM wParam, LPARAM lParam) { UNREFERENCED_PARAMETER(lParam); switch(wParam) { // function keys case VK_F1: return KEY_F1; case VK_F2: return KEY_F2; case VK_F3: return KEY_F3; case VK_F4: return KEY_F4; case VK_F5: return KEY_F5; case VK_F6: return KEY_F6; case VK_F7: return KEY_F7; case VK_F8: return KEY_F8; case VK_F9: return KEY_F9; case VK_F10: return KEY_F10; case VK_F11: return KEY_F11; case VK_F12: return KEY_F12; // special keys case VK_HOME: return KEY_HOME; case VK_END: return KEY_END; case VK_PRIOR: return KEY_PAGEUP; case VK_NEXT: return KEY_PAGEDOWN; case VK_INSERT: return KEY_INSERT; case VK_DELETE: return KEY_DELETE; case VK_BACK: return KEY_BACKSPACE; case VK_TAB: return KEY_TAB; case VK_RETURN: return KEY_ENTER; case VK_PAUSE: return KEY_PAUSE; case VK_CAPITAL: return KEY_CAPSLOCK; case VK_ESCAPE: return KEY_ESCAPE; case VK_UP: return KEY_UP; case VK_DOWN: return KEY_DOWN; case VK_LEFT: return KEY_LEFT; case VK_RIGHT: return KEY_RIGHT; } return KEY_NONE; } // Convert mouse state to CPUT state //----------------------------------------------------------------------------- CPUTMouseState CPUTWindowWin::ConvertMouseState(WPARAM wParam) { CPUTMouseState eState=CPUT_MOUSE_NONE; if( wParam & MK_CONTROL) eState = (CPUTMouseState) (eState | static_cast(CPUT_MOUSE_CTRL_DOWN)); if( wParam & MK_SHIFT) eState = (CPUTMouseState) (eState | static_cast(CPUT_MOUSE_SHIFT_DOWN)); if( wParam & MK_LBUTTON) eState = (CPUTMouseState) (eState | static_cast(CPUT_MOUSE_LEFT_DOWN)); if( wParam & MK_MBUTTON) eState = (CPUTMouseState) (eState | static_cast(CPUT_MOUSE_MIDDLE_DOWN)); if( wParam & MK_RBUTTON) eState = (CPUTMouseState) (eState | static_cast(CPUT_MOUSE_RIGHT_DOWN)); return eState; } // Main message pump //----------------------------------------------------------------------------- int CPUTWindowWin::StartMessageLoop() { // // Message pump // MSG msg = { 0 }; bool fRunning = true; while(fRunning) { // PeekMessage() is a passthru on no events // so it allows us to render while no events are present if( PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) ) { if (msg.message == WM_QUIT) { PostQuitMessage(0); fRunning = false; } TranslateMessage( &msg ); DispatchMessage( &msg ); } else { // trigger render and other calls mCPUT->InnerExecutionLoop(); } } // // Drain out the rest of the message queue. // while( PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) ) { TranslateMessage( &msg ); DispatchMessage( &msg ); } if (UnregisterClass(mAppTitle.c_str(), mhInst) == 0) { HandleWin32Error(); } // // Set the window handle to NULL to indicate window shutdown is complete // mhWnd = NULL; // return code mAppClosedReturnCode = (int) msg.wParam; return mAppClosedReturnCode; } ================================================ FILE: CPUT/CPUT/CPUTWindowWin.h ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #ifndef __WINDOWWIN_H__ #define __WINDOWWIN_H__ #include "CPUT.h" #include "CPUTOSServicesWin.h" #include "CPUTResource.h" // win resource.h customized for CPUT #include #include // for character codes #include // for CString class #include // CString class // Forward declarations class CPUT; // OS-specific window class //----------------------------------------------------------------------------- class CPUTWindowWin { public: // construction CPUTWindowWin(); ~CPUTWindowWin(); // Creates a graphics-context friendly window CPUTResult Create(CPUT* cput, const cString WindowTitle, const int windowWidth, const int windowHeight, int windowX, int windowY); // Main windows message loop that handles and dispatches messages int StartMessageLoop(); int Destroy(); int ReturnCode(); // return the HWND/Window handle for the created window HWND GetHWnd() { return mhWnd;}; protected: HINSTANCE mhInst; // current instance HWND mhWnd; // window handle int mAppClosedReturnCode; // windows OS return code cString mAppTitle; // title put at top of window static CPUT* mCPUT; // CPUT reference for callbacks static bool mbMaxMinFullScreen; // Window creation helper functions ATOM MyRegisterClass(HINSTANCE hInstance); BOOL InitInstance(int nCmdShow, int windowWidth, int windowHeight, int windowX, int windowY); static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); // CPUT conversion helper functions static CPUTMouseState ConvertMouseState(WPARAM wParam); static CPUTKey ConvertKeyCode(WPARAM wParam, LPARAM lParam); static CPUTKey ConvertSpecialKeyCode(WPARAM wParam, LPARAM lParam); }; #endif //#ifndef __WINDOWWIN_H__ ================================================ FILE: CPUT/CPUT/CPUT_DX11.cpp ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #include "CPUT_DX11.h" #include "CPUTRenderStateBlockDX11.h" #include "CPUTBufferDX11.h" #include "CPUTTextureDX11.h" // static initializers ID3D11Device* CPUT_DX11::mpD3dDevice = NULL; CPUT_DX11 *gpSample; // Destructor //----------------------------------------------------------------------------- CPUT_DX11::~CPUT_DX11() { // all previous shutdown tasks should have happened in CPUTShutdown() // We created the default renderstate block, we release it. CPUTRenderStateBlock *pRenderState = CPUTRenderStateBlock::GetDefaultRenderStateBlock(); SAFE_RELEASE(pRenderState); SAFE_RELEASE(mpPerFrameConstantBuffer); SAFE_RELEASE(mpBackBufferSRV); SAFE_RELEASE(mpBackBufferUAV); SAFE_RELEASE(mpBackBuffer); SAFE_RELEASE(mpDepthBuffer); SAFE_RELEASE(mpBackBufferTexture); SAFE_RELEASE(mpDepthBufferTexture); SAFE_RELEASE(mpDepthStencilSRV); // destroy the window if(mpWindow) { delete mpWindow; mpWindow = NULL; } SAFE_DELETE(mpTimer); DestroyDXContext(); } // initialize the CPUT system //----------------------------------------------------------------------------- CPUTResult CPUT_DX11::CPUTInitialize(const cString pCPUTResourcePath) { // set where CPUT will look for it's button images, fonts, etc return SetCPUTResourceDirectory(pCPUTResourcePath); } // Set where CPUT will look for it's button images, fonts, etc //----------------------------------------------------------------------------- CPUTResult CPUT_DX11::SetCPUTResourceDirectory(const cString ResourceDirectory) { // check to see if the specified directory is valid CPUTResult result = CPUT_SUCCESS; // resolve the directory to a full path cString fullPath; CPUTOSServices *pServices = CPUTOSServices::GetOSServices(); result = pServices->ResolveAbsolutePathAndFilename(ResourceDirectory, &fullPath); if(CPUTFAILED(result)) { return result; } // check existence of directory result = pServices->DoesDirectoryExist(fullPath); if(CPUTFAILED(result)) { return result; } // set the resource directory (absolute path) mResourceDirectory = fullPath; // tell the gui system where to look for it's resources // todo: do we want to force a flush/reload of all resources (i.e. change control graphics) result = CPUTGuiControllerDX11::GetController()->SetResourceDirectory(ResourceDirectory); return result; } // Handle keyboard events //----------------------------------------------------------------------------- CPUTEventHandledCode CPUT_DX11::CPUTHandleKeyboardEvent(CPUTKey key) { // dispatch event to GUI to handle GUI triggers (if any) CPUTEventHandledCode handleCode = CPUTGuiControllerDX11::GetController()->HandleKeyboardEvent(key); // dispatch event to users HandleMouseEvent() method HEAPCHECK; handleCode = HandleKeyboardEvent(key); HEAPCHECK; return handleCode; } // Handle mouse events //----------------------------------------------------------------------------- CPUTEventHandledCode CPUT_DX11::CPUTHandleMouseEvent(int x, int y, int wheel, CPUTMouseState state) { // dispatch event to GUI to handle GUI triggers (if any) CPUTEventHandledCode handleCode = CPUTGuiControllerDX11::GetController()->HandleMouseEvent(x,y,wheel,state); // dispatch event to users HandleMouseEvent() method if it wasn't consumed by the GUI if(CPUT_EVENT_HANDLED != handleCode) { HEAPCHECK; handleCode = HandleMouseEvent(x,y,wheel,state); HEAPCHECK; } return handleCode; } // Call appropriate OS create window call //----------------------------------------------------------------------------- CPUTResult CPUT_DX11::MakeWindow(const cString WindowTitle, int windowWidth, int windowHeight, int windowX, int windowY) { CPUTResult result; HEAPCHECK; // if we have a window, destroy it if(mpWindow) { delete mpWindow; mpWindow = NULL; } HEAPCHECK; // create the OS window mpWindow = new CPUTWindowWin(); result = mpWindow->Create((CPUT*)this, WindowTitle, windowWidth, windowHeight, windowX, windowY); HEAPCHECK; return result; } // Return the current GUI controller //----------------------------------------------------------------------------- CPUTGuiControllerDX11* CPUT_DX11::CPUTGetGuiController() { return CPUTGuiControllerDX11::GetController(); } // Create a DX11 context //----------------------------------------------------------------------------- CPUTResult CPUT_DX11::CreateDXContext(CPUTWindowCreationParams params) { HRESULT hr = S_OK; CPUTResult result = CPUT_SUCCESS; // window params RECT rc; HWND hWnd = mpWindow->GetHWnd(); GetClientRect( hWnd, &rc ); UINT width = rc.right - rc.left; UINT height = rc.bottom - rc.top; // set up DirectX creation parameters mdriverType = D3D_DRIVER_TYPE_NULL; mfeatureLevel = D3D_FEATURE_LEVEL_11_0; mpD3dDevice = NULL; mpContext = NULL; mpSwapChain = NULL; mSwapChainBufferCount = params.deviceParams.swapChainBufferCount; mpBackBufferRTV = NULL; UINT createDeviceFlags = 0; #ifdef _DEBUG createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG; #endif D3D_DRIVER_TYPE driverTypes[] = { D3D_DRIVER_TYPE_HARDWARE, D3D_DRIVER_TYPE_WARP, D3D_DRIVER_TYPE_REFERENCE, }; UINT numDriverTypes = ARRAYSIZE( driverTypes ); // SRV's (shader resource views) require Structured Buffer // usage (D3D11_RESOURCE_MISC_BUFFER_STRUCTURED) which was // introduced in shader model 5 (directx 11.0) // D3D_FEATURE_LEVEL featureLevels[] = { D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0 }; UINT numFeatureLevels = ARRAYSIZE( featureLevels ); // swap chain information DXGI_SWAP_CHAIN_DESC sd; ZeroMemory( &sd, sizeof( sd ) ); sd.BufferCount = mSwapChainBufferCount; sd.BufferDesc.Width = width; sd.BufferDesc.Height = height; mSwapChainFormat = params.deviceParams.swapChainFormat; sd.BufferDesc.Format = params.deviceParams.swapChainFormat; sd.BufferDesc.RefreshRate.Numerator = params.deviceParams.refreshRate; sd.BufferDesc.RefreshRate.Denominator = 1; sd.BufferUsage = params.deviceParams.swapChainUsage; sd.OutputWindow = hWnd; sd.SampleDesc.Count = 1; // Number of MSAA samples sd.SampleDesc.Quality = 0; sd.Windowed = !params.startFullscreen; sd.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH; // set the vsync parameter mSyncInterval = (0 != params.deviceParams.refreshRate)? 1 : 0; // walk devices and create device and swap chain on best matching piece of hardware bool functionalityTestPassed = false; for( UINT driverTypeIndex = 0; driverTypeIndex < numDriverTypes; driverTypeIndex++ ) { mdriverType = driverTypes[driverTypeIndex]; hr = D3D11CreateDeviceAndSwapChain( NULL, mdriverType, NULL, createDeviceFlags, featureLevels, numFeatureLevels, D3D11_SDK_VERSION, &sd, &mpSwapChain, &mpD3dDevice, &mfeatureLevel, &mpContext ); if( SUCCEEDED( hr ) ) { functionalityTestPassed = TestContextForRequiredFeatures(); if(true == functionalityTestPassed) { break; } else { // context was created, but failed to have required features // release and destroy this context and created resources SAFE_RELEASE(mpSwapChain); SAFE_RELEASE(mpContext); SAFE_RELEASE(mpD3dDevice); } } } ASSERT( (SUCCEEDED(hr) && (true==functionalityTestPassed)), _L("Failed creating device and swap chain.") ); if(!SUCCEEDED(hr) || !functionalityTestPassed) { CPUTOSServices::GetOSServices()->OpenMessageBox(_L("Required DirectX hardware support not present"), _L("Your system does not support the DirectX feature levels required for this sample.")); exit(1); // exit app directly } // If the WARP or Reference rasterizer is being used, the performance is probably terrible. // we throw up a dialog right after drawing the loading screen in CPUTCreateWindowAndContext // warning about that perf problem // call the DeviceCreated callback/backbuffer/etc creation result = CreateContext(); CPUTRenderStateBlock *pBlock = new CPUTRenderStateBlockDX11(); pBlock->CreateNativeResources(); CPUTRenderStateBlock::SetDefaultRenderStateBlock( pBlock ); // Create the per-frame constant buffer. D3D11_BUFFER_DESC bd = {0}; bd.ByteWidth = sizeof(CPUTFrameConstantBuffer); bd.BindFlags = D3D11_BIND_CONSTANT_BUFFER; bd.Usage = D3D11_USAGE_DYNAMIC; bd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; ID3D11Buffer *pPerFrameConstantBuffer; hr = (CPUT_DX11::GetDevice())->CreateBuffer( &bd, NULL, &pPerFrameConstantBuffer ); ASSERT( !FAILED( hr ), _L("Error creating constant buffer.") ); CPUTSetDebugName( pPerFrameConstantBuffer, _L("Per-Frame Constant buffer") ); cString name = _L("$cbPerFrameValues"); mpPerFrameConstantBuffer = new CPUTBufferDX11( name, pPerFrameConstantBuffer ); CPUTAssetLibrary::GetAssetLibrary()->AddConstantBuffer( name, mpPerFrameConstantBuffer ); SAFE_RELEASE(pPerFrameConstantBuffer); // We're done with it. The CPUTBuffer now owns it. return result; } // This function tests a created DirectX context for specific features required for // the framework, and possibly sample. If your sample has specific hw features // you wish to check for at creation time, you can add them here and have them // tested at startup time. If no contexts support your desired features, then // the system will revert to the DX reference rasterizer, or barring that, // pop up a dialog and exit. //----------------------------------------------------------------------------- bool CPUT_DX11::TestContextForRequiredFeatures() { // D3D11_RESOURCE_MISC_BUFFER_STRUCTURED check // attempt to create a // create the buffer for the shader resource view D3D11_BUFFER_DESC desc; ZeroMemory( &desc, sizeof(desc) ); desc.Usage = D3D11_USAGE_DEFAULT; // set the stride for one 'element' block of verts UINT m_VertexStride = 4*sizeof(float); // size in bytes of a single element - this test case we'll use 4 floats desc.ByteWidth = 1 * m_VertexStride; // size in bytes of entire buffer - this test case uses just one element desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; desc.CPUAccessFlags = 0; desc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED; desc.StructureByteStride = m_VertexStride; ID3D11Buffer *pVertexBufferForSRV=NULL; D3D11_SUBRESOURCE_DATA resourceData; float pData[4] ={ 0.0f, 0.0f, 0.0f, 0.0f }; ZeroMemory( &resourceData, sizeof(resourceData) ); resourceData.pSysMem = pData; HRESULT hr = mpD3dDevice->CreateBuffer( &desc, &resourceData, &pVertexBufferForSRV ); SAFE_RELEASE(pVertexBufferForSRV); if(!SUCCEEDED(hr)) { // failed the feature test return false; } // add other required features here return true; } // Return the active D3D device used to create the context //----------------------------------------------------------------------------- ID3D11Device* CPUT_DX11::GetDevice() { return mpD3dDevice; } // Default creation routine for making the back/stencil buffers //----------------------------------------------------------------------------- CPUTResult CPUT_DX11::CreateContext() { HRESULT hr; CPUTResult result; RECT rc; HWND hWnd = mpWindow->GetHWnd(); GetClientRect( hWnd, &rc ); UINT width = rc.right - rc.left; UINT height = rc.bottom - rc.top; // Create a render target view ID3D11Texture2D *pBackBuffer = NULL; hr = mpSwapChain->GetBuffer( 0, __uuidof( ID3D11Texture2D ), ( LPVOID* )&pBackBuffer ); ASSERT( SUCCEEDED(hr), _L("Failed getting back buffer.") ); hr = mpD3dDevice->CreateRenderTargetView( pBackBuffer, NULL, &mpBackBufferRTV ); pBackBuffer->Release(); ASSERT( SUCCEEDED(hr), _L("Failed creating render target view.") ); CPUTSetDebugName( mpBackBufferRTV, _L("BackBufferView") ); // create depth/stencil buffer result = CreateAndBindDepthBuffer(width, height); ASSERT( SUCCEEDED(hr), _L("Failed creating and binding depth buffer.") ); // Setup the viewport D3D11_VIEWPORT vp; vp.Width = (FLOAT)width; vp.Height = (FLOAT)height; vp.MinDepth = 0.0f; vp.MaxDepth = 1.0f; vp.TopLeftX = 0; vp.TopLeftY = 0; mpContext->RSSetViewports( 1, &vp ); return CPUT_SUCCESS; } // destroy the DX context and release all resources //----------------------------------------------------------------------------- CPUTResult CPUT_DX11::DestroyDXContext() { if (mpContext) { mpContext->ClearState(); mpContext->Flush(); } SAFE_RELEASE( mpBackBufferRTV ); SAFE_RELEASE( mpDepthStencilBuffer ); SAFE_RELEASE( mpDepthStencilState ); SAFE_RELEASE( mpDepthStencilView ); SAFE_RELEASE( mpContext ); SAFE_RELEASE( mpD3dDevice ); SAFE_RELEASE( mpSwapChain ); return CPUT_SUCCESS; } // Toggle the fullscreen mode // This routine keeps the current desktop resolution. DougB suggested allowing // one to go fullscreen in a different resolution //----------------------------------------------------------------------------- CPUTResult CPUT_DX11::CPUTToggleFullScreenMode() { // get the current fullscreen state bool bIsFullscreen = CPUTGetFullscreenState(); // toggle the state bIsFullscreen = !bIsFullscreen; // set the fullscreen state HRESULT hr = mpSwapChain->SetFullscreenState(bIsFullscreen, NULL); ASSERT( SUCCEEDED(hr), _L("Failed toggling full screen mode.") ); // trigger resize event so that all buffers can resize int x,y,width,height; CPUTOSServices::GetOSServices()->GetClientDimensions(&x, &y, &width, &height); ResizeWindow(width,height); // trigger a fullscreen mode change call if the sample has decided to handle the mode change FullscreenModeChange( bIsFullscreen ); return CPUT_SUCCESS; } // Set the fullscreen mode to a desired state //----------------------------------------------------------------------------- void CPUT_DX11::CPUTSetFullscreenState(bool bIsFullscreen) { // get the current fullscreen state bool bCurrentFullscreenState = CPUTGetFullscreenState(); if((bool)bCurrentFullscreenState == bIsFullscreen) { // no need to call expensive state change, full screen state is already // in desired state return; } // set the fullscreen state HRESULT hr = mpSwapChain->SetFullscreenState(bIsFullscreen, NULL); ASSERT( SUCCEEDED(hr), _L("Failed toggling full screen mode.") ); // trigger resize event so that all buffers can resize int x,y,width,height; CPUTOSServices::GetOSServices()->GetClientDimensions(&x, &y, &width, &height); ResizeWindow(width,height); // trigger a fullscreen mode change call if the sample has decided to handle the mode change FullscreenModeChange( bIsFullscreen ); } // Get a bool indicating whether the system is in full screen mode or not //----------------------------------------------------------------------------- bool CPUT_DX11::CPUTGetFullscreenState() { // get the current fullscreen state BOOL bCurrentlyFullscreen; IDXGIOutput *pSwapTarget=NULL; mpSwapChain->GetFullscreenState(&bCurrentlyFullscreen, &pSwapTarget); SAFE_RELEASE(pSwapTarget); if(TRUE == bCurrentlyFullscreen ) { return true; } return false; } // Create the depth buffer //----------------------------------------------------------------------------- CPUTResult CPUT_DX11::CreateAndBindDepthBuffer(int width, int height) { HRESULT hr; // Clamp to minimum size of 1x1 pixel width = max( width, 1 ); height = max( height, 1 ); // ---- DEPTH BUFFER --- // 1. Initialize the description of the depth buffer. D3D11_TEXTURE2D_DESC depthBufferDesc; ZeroMemory(&depthBufferDesc, sizeof(depthBufferDesc)); // Set up the description of the depth buffer. depthBufferDesc.Width = width; depthBufferDesc.Height = height; depthBufferDesc.MipLevels = 1; depthBufferDesc.ArraySize = 1; depthBufferDesc.Format = DXGI_FORMAT_R32_TYPELESS; depthBufferDesc.SampleDesc.Count = 1; depthBufferDesc.SampleDesc.Quality = 0; depthBufferDesc.Usage = D3D11_USAGE_DEFAULT; depthBufferDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL | D3D11_BIND_SHADER_RESOURCE; depthBufferDesc.CPUAccessFlags = 0; depthBufferDesc.MiscFlags = 0; // Create the texture for the depth buffer using the filled out description. hr = mpD3dDevice->CreateTexture2D(&depthBufferDesc, NULL, &mpDepthStencilBuffer); ASSERT( SUCCEEDED(hr), _L("Failed to create texture.") ); CPUTSetDebugName( mpDepthStencilBuffer, _L("DepthBufferTexture") ); // 2. Initialize the description of the stencil state. D3D11_DEPTH_STENCIL_DESC depthStencilDesc; ZeroMemory(&depthStencilDesc, sizeof(depthStencilDesc)); // Set up the description of the stencil state. depthStencilDesc.DepthEnable = true; depthStencilDesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL; depthStencilDesc.DepthFunc = D3D11_COMPARISON_GREATER_EQUAL; depthStencilDesc.StencilEnable = true; depthStencilDesc.StencilReadMask = 0xFF; depthStencilDesc.StencilWriteMask = 0xFF; // Stencil operations if pixel is front-facing. depthStencilDesc.FrontFace.StencilFailOp = D3D11_STENCIL_OP_KEEP; depthStencilDesc.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_INCR; depthStencilDesc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP; depthStencilDesc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS; // Stencil operations if pixel is back-facing. depthStencilDesc.BackFace.StencilFailOp = D3D11_STENCIL_OP_KEEP; depthStencilDesc.BackFace.StencilDepthFailOp = D3D11_STENCIL_OP_DECR; depthStencilDesc.BackFace.StencilPassOp = D3D11_STENCIL_OP_KEEP; depthStencilDesc.BackFace.StencilFunc = D3D11_COMPARISON_ALWAYS; // Create the depth stencil state. hr = mpD3dDevice->CreateDepthStencilState(&depthStencilDesc, &mpDepthStencilState); ASSERT( SUCCEEDED(hr), _L("Failed to create depth-stencil state.") ); mpContext->OMSetDepthStencilState(mpDepthStencilState, 1); // Create shader resource view D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc; ZeroMemory(&srvDesc, sizeof(srvDesc)); srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; srvDesc.Texture2D.MipLevels = 1; srvDesc.Format = DXGI_FORMAT_R32_FLOAT; hr = mpD3dDevice->CreateShaderResourceView(mpDepthStencilBuffer, &srvDesc, &mpDepthStencilSRV); ASSERT( SUCCEEDED(hr), _L("Failed to create depth-stencil SRV.") ); CPUTSetDebugName( mpDepthStencilSRV, _L("DepthStencilSRV") ); // Create the depth stencil view. D3D11_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc; ZeroMemory(&depthStencilViewDesc, sizeof(depthStencilViewDesc)); depthStencilViewDesc.Format = DXGI_FORMAT_D32_FLOAT; depthStencilViewDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D; depthStencilViewDesc.Texture2D.MipSlice = 0; hr = mpD3dDevice->CreateDepthStencilView(mpDepthStencilBuffer, &depthStencilViewDesc, &mpDepthStencilView); ASSERT( SUCCEEDED(hr), _L("Failed to create depth-stencil view.") ); CPUTSetDebugName( mpDepthStencilView, _L("DepthStencilView") ); // Bind the render target view and depth stencil buffer to the output render pipeline. mpContext->OMSetRenderTargets(1, &mpBackBufferRTV, mpDepthStencilView); CPUTRenderTargetColor::SetActiveRenderTargetView( mpBackBufferRTV ); CPUTRenderTargetDepth::SetActiveDepthStencilView( mpDepthStencilView ); return CPUT_SUCCESS; } // incoming resize event to be handled and translated //----------------------------------------------------------------------------- void CPUT_DX11::ResizeWindow(UINT width, UINT height) { HRESULT hr; CPUTResult result; CPUTAssetLibraryDX11 *pAssetLibrary = (CPUTAssetLibraryDX11*)CPUTAssetLibraryDX11::GetAssetLibrary(); // TODO: Making the back and depth buffers into CPUTRenderTargets should simplify this (CPUTRenderTarget* manages RTV, SRV, UAV, etc.) if( mpBackBuffer ) ((CPUTBufferDX11*)mpBackBuffer)->ReleaseBuffer(); if( mpDepthBuffer ) ((CPUTBufferDX11*)mpDepthBuffer)->ReleaseBuffer(); if( mpBackBufferTexture ) ((CPUTTextureDX11*)mpBackBufferTexture)->ReleaseTexture(); if( mpDepthBufferTexture ) ((CPUTTextureDX11*)mpDepthBufferTexture)->ReleaseTexture(); // Make sure we don't have any buffers bound. mpContext->ClearState(); Present(); mpContext->Flush(); SAFE_RELEASE(mpBackBufferRTV); SAFE_RELEASE(mpBackBufferSRV); SAFE_RELEASE(mpBackBufferUAV); SAFE_RELEASE(mpDepthStencilSRV); CPUT::ResizeWindow( width, height ); // Call the sample's clean up code if present. ReleaseSwapChain(); // handle the internals of a resize int windowWidth, windowHeight; CPUTOSServices *pServices = CPUTOSServices::GetOSServices(); pServices->GetClientDimensions( &windowWidth, &windowHeight); // resize the swap chain hr = mpSwapChain->ResizeBuffers(mSwapChainBufferCount, windowWidth, windowHeight, mSwapChainFormat, 0); ASSERT( SUCCEEDED(hr), _L("Error resizing swap chain") ); // re-create the render-target view ID3D11Texture2D *pSwapChainBuffer = NULL; hr = mpSwapChain->GetBuffer( 0, __uuidof( ID3D11Texture2D), (LPVOID*) (&pSwapChainBuffer)); ASSERT(SUCCEEDED(hr), _L("")); hr = mpD3dDevice->CreateRenderTargetView( pSwapChainBuffer, NULL, &mpBackBufferRTV); ASSERT(SUCCEEDED(hr), _L("")); hr = mpD3dDevice->CreateShaderResourceView( pSwapChainBuffer, NULL, &mpBackBufferSRV); ASSERT(SUCCEEDED(hr), _L("")); #ifdef CREATE_SWAP_CHAIN_UAV // Not every DXGI format supports UAV. So, create UAV only if sample chooses to do so. hr = mpD3dDevice->CreateUnorderedAccessView( pSwapChainBuffer, NULL, &mpBackBufferUAV); ASSERT(SUCCEEDED(hr), _L("")); #endif // Add the back buffer to the asset library. Create CPUTBuffer and a CPUTTexture forms and add them. if( mpBackBuffer ) { ((CPUTBufferDX11*)mpBackBuffer)->SetBufferAndViews( NULL, mpBackBufferSRV, mpBackBufferUAV ); } else { cString backBufferName = _L("$BackBuffer"); mpBackBuffer = new CPUTBufferDX11( backBufferName, NULL, mpBackBufferUAV ); pAssetLibrary->AddBuffer( backBufferName, mpBackBuffer ); } if( mpBackBufferTexture ) { ((CPUTTextureDX11*)mpBackBufferTexture)->SetTextureAndShaderResourceView( NULL, mpBackBufferSRV ); } else { cString backBufferName = _L("$BackBuffer"); mpBackBufferTexture = new CPUTTextureDX11( backBufferName, NULL, mpBackBufferSRV ); pAssetLibrary->AddTexture( backBufferName, mpBackBufferTexture ); } // release the old depth buffer objects // release the temporary swap chain buffer SAFE_RELEASE(pSwapChainBuffer); SAFE_RELEASE(mpDepthStencilBuffer); SAFE_RELEASE(mpDepthStencilState); SAFE_RELEASE(mpDepthStencilView); result = CreateAndBindDepthBuffer(windowWidth, windowHeight); if(CPUTFAILED(result)) { // depth buffer creation error ASSERT(0,_L("")); } if( mpDepthBuffer ) { ((CPUTBufferDX11*)mpDepthBuffer)->SetBufferAndViews( NULL, mpDepthStencilSRV, NULL ); } else { cString depthBufferName = _L("$DepthBuffer"); mpDepthBuffer = new CPUTBufferDX11( depthBufferName, NULL, mpDepthStencilSRV ); pAssetLibrary->AddBuffer( depthBufferName, mpDepthBuffer ); } if( mpDepthBufferTexture ) { ((CPUTTextureDX11*)mpDepthBufferTexture)->SetTextureAndShaderResourceView( NULL, mpDepthStencilSRV ); } else { cString DepthBufferName = _L("$DepthBuffer"); mpDepthBufferTexture = new CPUTTextureDX11( DepthBufferName, NULL, mpDepthStencilSRV ); pAssetLibrary->AddTexture( DepthBufferName, mpDepthBufferTexture ); } // Release our extra reference to each view. // if(mpBackBufferSRV) mpBackBufferSRV->Release(); // if(mpBackBufferUAV) mpBackBufferUAV->Release(); // if(mpDepthStencilSRV) mpDepthStencilSRV->Release();; // set the viewport D3D11_VIEWPORT vp; vp.Width = (FLOAT) windowWidth; vp.Height = (FLOAT)windowHeight; vp.MinDepth = 0.0f; vp.MaxDepth = 1.0f; vp.TopLeftX = 0; vp.TopLeftY = 0; mpContext->RSSetViewports( 1, &vp ); // trigger the GUI manager to resize CPUTGuiControllerDX11::GetController()->Resize(); } // 'soft' resize - just stretch-blit //----------------------------------------------------------------------------- void CPUT_DX11::ResizeWindowSoft(UINT width, UINT height) { UNREFERENCED_PARAMETER(width); UNREFERENCED_PARAMETER(height); // trigger the GUI manager to resize CPUTGuiControllerDX11::GetController()->Resize(); InnerExecutionLoop(); } //----------------------------------------------------------------------------- void CPUT_DX11::SetPerFrameConstantBuffer( double totalSeconds ) { if( mpPerFrameConstantBuffer ) { ID3D11Buffer *pBuffer = mpPerFrameConstantBuffer->GetNativeBuffer(); // update parameters of constant buffer D3D11_MAPPED_SUBRESOURCE mapInfo; mpContext->Map( pBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapInfo ); { // TODO: remove construction of XMM type CPUTFrameConstantBuffer *pCb = (CPUTFrameConstantBuffer*)mapInfo.pData; CPUTCamera *pCamera = gpSample->GetCamera(); if( pCamera ) { pCb->View = XMMATRIX((float*)pCamera->GetViewMatrix()); pCb->Projection = XMMATRIX((float*)pCamera->GetProjectionMatrix()); } pCb->LightColor = XMLoadFloat3(&XMFLOAT3((float*)&mLightColor)); // TODO: Get from light float totalSecondsFloat = (float)totalSeconds; pCb->TotalSeconds = XMLoadFloat(&totalSecondsFloat); pCb->AmbientColor = XMLoadFloat3(&XMFLOAT3((float*)&mAmbientColor)); } mpContext->Unmap(pBuffer,0); } } // Call the user's Render() callback (if it exists) //----------------------------------------------------------------------------- void CPUT_DX11::InnerExecutionLoop() { #ifdef CPUT_GPA_INSTRUMENTATION D3DPERF_BeginEvent(D3DCOLOR(0xff0000), L"CPUT User's Render() "); #endif if(!mbShutdown) { if( mpSwapChain ) { double deltaSeconds = mpTimer->GetElapsedTime(); Update(deltaSeconds); Present(); // Note: Presenting immediately before Rendering minimizes CPU stalls (i.e., execute Update() before Present() stalls) double totalSeconds = mpTimer->GetTotalTime(); SetPerFrameConstantBuffer(totalSeconds); CPUTMaterialDX11::ResetStateTracking(); Render(deltaSeconds); } if(!CPUTOSServices::GetOSServices()->DoesWindowHaveFocus()) { Sleep(100); } } else { #ifndef _DEBUG exit(0); #endif Present(); // Need to present, or will leak all references held by previous Render()! ShutdownAndDestroy(); } #ifdef CPUT_GPA_INSTRUMENTATION D3DPERF_EndEvent(); #endif } // draw all the GUI controls //----------------------------------------------------------------------------- void CPUT_DX11::CPUTDrawGUI() { #ifdef CPUT_GPA_INSTRUMENTATION D3DPERF_BeginEvent(D3DCOLOR(0xff0000), L"CPUT Draw GUI"); #endif // draw all the Gui controls HEAPCHECK; CPUTGuiControllerDX11::GetController()->Draw(mpContext); HEAPCHECK; #ifdef CPUT_GPA_INSTRUMENTATION D3DPERF_EndEvent(); #endif } // Parse the command line for the parameters // Only parameters that are specified on the command line are updated, if there // are no parameters for a value, the previous WindowParams settings passed in // are preserved //----------------------------------------------------------------------------- CPUTResult CPUT_DX11::CPUTParseCommandLine(cString commandLine, CPUTWindowCreationParams *pWindowParams, cString *pFilename) { ASSERT( (NULL!=pWindowParams), _L("Required command line parsing parameter is NULL")); ASSERT( (NULL!=pFilename), _L("Required command line parsing parameter is NULL")); // there are no command line parameters, just return if(0==commandLine.size()) { return CPUT_SUCCESS; } // we do have parameters, so parse them. #if defined (UNICODE) || defined(_UNICODE) // convert command line to lowercase version cString CommandLineParams(commandLine); std::transform(CommandLineParams.begin(), CommandLineParams.end(), CommandLineParams.begin(), ::tolower); // special case - double-clicked on a file // In the case someone has associated .set files with CPUT, then the target set file comes in surrounded // by quote marks (i.e. "c:\mySample\Asset\City.set"). If the first char is a quote mark, we're in this // special case if('"' == CommandLineParams[0]) { pFilename->assign(&CommandLineParams[1]); // remove the leading " char (*pFilename)[pFilename->size()-1] = '\0'; // remove the trailing " char return CPUT_SUCCESS; } wchar_t separators[] = L" \t\n"; wchar_t* nextToken = NULL; wchar_t* token = wcstok_s((wchar_t*)CommandLineParams.c_str(), separators, &nextToken); while(token) { if('-' == token[0]) { // parameter - get next token for which one cString ParameterName(&token[1]); if(0!=ParameterName.size()) { if(0==ParameterName.compare(_L("width"))) { // get the next value token = wcstok_s(NULL, separators, &nextToken); ASSERT(token, _L("-width command line parameter missing required numerical value")); pWindowParams->windowWidth = _wtoi(token); } else if(0==ParameterName.compare(_L("height"))) { // get the next value token = wcstok_s(NULL, separators, &nextToken); ASSERT(token, _L("-height command line parameter missing required numerical value")); pWindowParams->windowHeight = _wtoi(token); } else if(0==ParameterName.compare(_L("fullscreen"))) { // get the bool token = wcstok_s(NULL, separators, &nextToken); cString boolString(token); if(0==boolString.compare(_L("true"))) { pWindowParams->startFullscreen = true; } } else if(0==ParameterName.compare(_L("vsync"))) { // get the bool token = wcstok_s(NULL, separators, &nextToken); cString boolString(token); if( (0==boolString.compare(_L("on"))) || (0==boolString.compare(_L("true"))) ) { // vsync set to 30 FPS pWindowParams->deviceParams.refreshRate = 30; } if( (0==boolString.compare(_L("off"))) || (0==boolString.compare(_L("false"))) ) { pWindowParams->deviceParams.refreshRate = 0; } } else if(0==ParameterName.compare(_L("xpos"))) { // get the next value token = wcstok_s(NULL, separators, &nextToken); ASSERT(token, _L("-xpos command line parameter missing required numerical value")); pWindowParams->windowPositionX = _wtoi(token); } else if(0==ParameterName.compare(_L("ypos"))) { // get the next value token = wcstok_s(NULL, separators, &nextToken); ASSERT(token, _L("-ypos command line parameter missing required numerical value")); pWindowParams->windowPositionY = _wtoi(token); } else if(0==ParameterName.compare(_L("file"))) { // get the filename token = wcstok_s(NULL, separators, &nextToken); pFilename->assign(token); } else { // we don't know what the string was, but all specified ones should be of the form // '- ' // so skip over the part token = wcstok_s(NULL, separators, &nextToken); } // we don't know what this parameter is, let user parse it } } // advance to next token token = wcstok_s(NULL, separators, &nextToken); } #else ASSERT(false, _L("CPUT_DX11::CPUTParseCommandLine non-UNICODE version not written yet - need to write if we want to support multi-byte")); #endif return CPUT_SUCCESS; } // Create a window context //----------------------------------------------------------------------------- CPUTResult CPUT_DX11::CPUTCreateWindowAndContext(const cString WindowTitle, CPUTWindowCreationParams windowParams) { CPUTResult result = CPUT_SUCCESS; HEAPCHECK; // create the window result = MakeWindow(WindowTitle, windowParams.windowWidth, windowParams.windowHeight, windowParams.windowPositionX, windowParams.windowPositionY); if(CPUTFAILED(result)) { return result; } HEAPCHECK; // create the DX context result = CreateDXContext(windowParams); if(CPUTFAILED(result)) { return result; } HEAPCHECK; #define ENABLE_GUI #ifdef ENABLE_GUI // initialize the gui controller // Use the ResourceDirectory that was given during the Initialize() function // to locate the GUI+font resources CPUTGuiControllerDX11 *pGUIController = CPUTGuiControllerDX11::GetController(); cString ResourceDirectory = GetCPUTResourceDirectory(); result = pGUIController->Initialize(mpContext, ResourceDirectory); if(CPUTFAILED(result)) { return result; } // register the callback object for GUI events as our sample CPUTGuiControllerDX11::GetController()->SetCallback(this); #endif HEAPCHECK; DrawLoadingFrame(); HEAPCHECK; // warn the user they are using the software rasterizer if((D3D_DRIVER_TYPE_REFERENCE == mdriverType) || (D3D_DRIVER_TYPE_WARP == mdriverType)) { CPUTOSServices::GetOSServices()->OpenMessageBox(_L("Performance warning"), _L("Your graphics hardware does not support the DirectX features required by this sample. The sample is now running using the DirectX software rasterizer.")); } // trigger a post-create user callback event HEAPCHECK; Create(); HEAPCHECK; // // Start the timer after everything is initialized and assets have been loaded // mpTimer->StartTimer(); // if someone triggers the shutdown routine in on-create, exit if(mbShutdown) { return result; } // fill first frame with clear values so render order later is ok const float srgbClearColor[] = { 0.0993f, 0.0993f, 0.0993f, 1.0f }; mpContext->ClearRenderTargetView( mpBackBufferRTV, srgbClearColor ); mpContext->ClearDepthStencilView(mpDepthStencilView, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 0.0f, 0); // trigger a 'resize' event int x,y,width,height; CPUTOSServices::GetOSServices()->GetClientDimensions(&x, &y, &width, &height); ResizeWindow(width,height); return result; } // Pop up a message box with specified title/text //----------------------------------------------------------------------------- void CPUT_DX11::DrawLoadingFrame() { // fill first frame with clear values so render order later is ok const float srgbClearColor[] = { 0.0993f, 0.0993f, 0.0993f, 1.0f }; mpContext->ClearRenderTargetView( mpBackBufferRTV, srgbClearColor ); mpContext->ClearDepthStencilView(mpDepthStencilView, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 0.0f, 0); // get center int x,y,width,height; CPUTOSServices::GetOSServices()->GetClientDimensions(&x, &y, &width, &height); // draw "loading..." text CPUTGuiControllerDX11 *pGUIController = CPUTGuiControllerDX11::GetController(); CPUTText *pText = NULL; pGUIController->CreateText(_L("Just a moment, now loading..."), 999, 0, &pText); pGUIController->EnableAutoLayout(false); int textWidth, textHeight; pText->GetDimensions(textWidth, textHeight); pText->SetPosition(width/2-textWidth/2, height/2); pGUIController->Draw(mpContext); pGUIController->DeleteAllControls(); pGUIController->EnableAutoLayout(true); // present loading screen mpSwapChain->Present( mSyncInterval, 0 ); } // Pop up a message box with specified title/text //----------------------------------------------------------------------------- void CPUT_DX11::CPUTMessageBox(const cString DialogBoxTitle, const cString DialogMessage) { CPUTOSServices::GetOSServices()->OpenMessageBox(DialogBoxTitle.c_str(), DialogMessage.c_str()); } // start main message loop //----------------------------------------------------------------------------- int CPUT_DX11::CPUTMessageLoop() { #ifdef CPUT_GPA_INSTRUMENTATION D3DPERF_BeginEvent(D3DCOLOR(0xff0000), L"CPUTMessageLoop"); #endif return mpWindow->StartMessageLoop(); #ifdef CPUT_GPA_INSTRUMENTATION D3DPERF_EndEvent(); #endif } // Window is closing. Shut the system to shut down now, not later. //----------------------------------------------------------------------------- void CPUT_DX11::DeviceShutdown() { if(mpSwapChain) { // DX requires setting fullscreenstate to false before exit. mpSwapChain->SetFullscreenState(false, NULL); } if(false == mbShutdown) { mbShutdown = true; ShutdownAndDestroy(); } } // Shutdown the CPUT system // Destroy all 'global' resource handling objects, all asset handlers, // the DX context, and everything EXCEPT the window //----------------------------------------------------------------------------- void CPUT_DX11::Shutdown() { // release the lock on the mouse (if there was one) CPUTOSServices::GetOSServices()->ReleaseMouse(); mbShutdown = true; } // Frees all resources and removes all assets from asset library //----------------------------------------------------------------------------- void CPUT_DX11::RestartCPUT() { // // Clear out all CPUT resources // CPUTInputLayoutCacheDX11::GetInputLayoutCache()->ClearLayoutCache(); CPUTAssetLibrary::GetAssetLibrary()->ReleaseAllLibraryLists(); CPUTGuiControllerDX11::GetController()->DeleteAllControls(); CPUTGuiControllerDX11::GetController()->ReleaseResources(); // // Clear out all DX resources and contexts // DestroyDXContext(); // // Signal the window to close // mpWindow->Destroy(); // // Clear out the timer // mpTimer->StopTimer(); mpTimer->ResetTimer(); HEAPCHECK; } // Actually destroy all 'global' resource handling objects, all asset handlers, // the DX context, and everything EXCEPT the window //----------------------------------------------------------------------------- void CPUT_DX11::ShutdownAndDestroy() { // make sure no more rendering can happen mbShutdown = true; // call the user's OnShutdown code Shutdown(); CPUTInputLayoutCacheDX11::DeleteInputLayoutCache(); CPUTAssetLibraryDX11::DeleteAssetLibrary(); CPUTGuiControllerDX11::DeleteController(); // #ifdef _DEBUG #if 0 ID3D11Debug *pDebug; mpD3dDevice->QueryInterface(IID_ID3D11Debug, (VOID**)(&pDebug)); if( pDebug ) { pDebug->ReportLiveDeviceObjects(D3D11_RLDO_DETAIL); pDebug->Release(); } #endif CPUTOSServices::DeleteOSServices(); // Tell the window layer to post a close-window message to OS // and stop the message pump mpWindow->Destroy(); HEAPCHECK; } //----------------------------------------------------------------------------- void CPUTSetDebugName( void *pResource, cString name ) { #ifdef _DEBUG char pCharString[CPUT_MAX_STRING_LENGTH]; const wchar_t *pWideString = name.c_str(); UINT ii; UINT length = min( (UINT)name.length(), (CPUT_MAX_STRING_LENGTH-1)); for(ii=0; iiSetPrivateData( WKPDID_D3DDebugObjectName, (UINT)name.length(), pCharString ); #endif // _DEBUG } ================================================ FILE: CPUT/CPUT/CPUT_DX11.h ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #ifndef __CPUT_DX11_H__ #define __CPUT_DX11_H__ #include // include base headers we'll need #include "CPUTWindowWin.h" #include "CPUT.h" #include "CPUTMath.h" #include "CPUTEventHandler.h" #include "CPUTGuiControllerDX11.h" // CPUT objects #include "CPUTMeshDX11.h" #include "CPUTModelDX11.h" #include "CPUTAssetSetDX11.h" #include "CPUTAssetLibraryDX11.h" #include "CPUTCamera.h" #include "CPUTLight.h" #include "CPUTMaterialDX11.h" // include all DX11 headers needed #include #include #include // for D3DX11CompileFromFile() #include // for D3DReflect() / D3DX11Refection - IMPORTANT NOTE: include directories MUST list DX SDK include path BEFORE // Windows include paths or you'll get compile errors with D3DShader.h // context creation parameters struct CPUTContextCreation { int refreshRate; int swapChainBufferCount; DXGI_FORMAT swapChainFormat; DXGI_USAGE swapChainUsage; }; // window creation parameters struct CPUTWindowCreationParams { bool startFullscreen; int windowWidth; int windowHeight; int windowPositionX; int windowPositionY; CPUTContextCreation deviceParams; CPUTWindowCreationParams() : startFullscreen(false), windowWidth(1280), windowHeight(720), windowPositionX(0), windowPositionY(0) {} }; // Types of message boxes you can create enum CPUT_MESSAGE_BOX_TYPE { CPUT_MB_OK = MB_OK | MB_ICONINFORMATION, CPUT_MB_ERROR = MB_OK | MB_ICONEXCLAMATION, CPUT_MB_WARNING = MB_OK | MB_ICONWARNING }; //-------------------------------------------------------------------------------------- struct CPUTFrameConstantBuffer { XMMATRIX View; XMMATRIX Projection; XMVECTOR AmbientColor; XMVECTOR LightColor; XMVECTOR TotalSeconds; }; // DirectX 11 CPUT layer //----------------------------------------------------------------------------- class CPUT_DX11; extern CPUT_DX11 *gpSample; class CPUT_DX11:public CPUT { protected: static ID3D11Device *mpD3dDevice; public: static ID3D11Device *GetDevice(); protected: CPUTWindowWin *mpWindow; bool mbShutdown; cString mResourceDirectory; D3D_DRIVER_TYPE mdriverType; D3D_FEATURE_LEVEL mfeatureLevel; ID3D11DeviceContext *mpContext; IDXGISwapChain *mpSwapChain; UINT mSwapChainBufferCount; ID3D11RenderTargetView *mpBackBufferRTV; ID3D11ShaderResourceView *mpBackBufferSRV; ID3D11UnorderedAccessView *mpBackBufferUAV; DXGI_FORMAT mSwapChainFormat; ID3D11Texture2D *mpDepthStencilBuffer; ID3D11DepthStencilState *mpDepthStencilState; ID3D11DepthStencilView *mpDepthStencilView; // was in protected ID3D11ShaderResourceView *mpDepthStencilSRV; UINT mSyncInterval; // used for vsync CPUTBufferDX11 *mpPerFrameConstantBuffer; public: CPUT_DX11():mpWindow(NULL), mpContext(NULL), mpSwapChain(NULL), mSwapChainBufferCount(1), mpBackBufferRTV(NULL), mpBackBufferSRV(NULL), mpBackBufferUAV(NULL), mpDepthStencilBuffer(NULL), mpDepthStencilState(NULL), mpDepthStencilView(NULL), mpDepthStencilSRV(NULL), mSwapChainFormat(DXGI_FORMAT_UNKNOWN), mbShutdown(false), mSyncInterval(0), // start with vsync off mpPerFrameConstantBuffer(NULL) { mpTimer = (CPUTTimer*) new CPUTTimerWin(); gpSample = this; } virtual ~CPUT_DX11(); // context creation/destruction routines CPUTResult CPUTInitialize(const cString ResourceDirectory); CPUTResult SetCPUTResourceDirectory(const cString ResourceDirectory); cString GetCPUTResourceDirectory() { return mResourceDirectory; } CPUTResult CPUTParseCommandLine(cString commandLine, CPUTWindowCreationParams *pWindowParams, cString *pFilename); D3D_FEATURE_LEVEL GetFeatureLevel() { return mfeatureLevel; } int CPUTMessageLoop(); CPUTResult CPUTCreateWindowAndContext(const cString WindowTitle, CPUTWindowCreationParams windowParams); // CPUT interfaces virtual void ResizeWindow(UINT width, UINT height); virtual void ResizeWindowSoft(UINT width, UINT height); void DeviceShutdown(); void RestartCPUT(); void SetPerFrameConstantBuffer( double totalSeconds ); void InnerExecutionLoop(); // events virtual void Update(double deltaSeconds) {} virtual void Present() { mpSwapChain->Present( mSyncInterval, 0 ); } virtual void Render(double deltaSeconds) = 0; virtual void Create()=0; virtual void Shutdown(); virtual void FullscreenModeChange(bool bFullscreen) {UNREFERENCED_PARAMETER(bFullscreen);} // fires when CPUT changes to/from fullscreen mode virtual void ReleaseSwapChain() {} // virtual void ResizeWindow(UINT width, UINT height){UNREFERENCED_PARAMETER(width);UNREFERENCED_PARAMETER(height);} virtual CPUTResult CreateContext(); // GUI void CPUTDrawGUI(); // Event Handling CPUTEventHandledCode CPUTHandleKeyboardEvent(CPUTKey key); CPUTEventHandledCode CPUTHandleMouseEvent(int x, int y, int wheel, CPUTMouseState state); // Utility functions for the sample developer CPUTResult CPUTToggleFullScreenMode(); void CPUTSetFullscreenState(bool bIsFullscreen); bool CPUTGetFullscreenState(); CPUTGuiControllerDX11* CPUTGetGuiController(); // Message boxes void CPUTMessageBox(const cString DialogBoxTitle, const cString DialogMessage); protected: // private helper functions bool TestContextForRequiredFeatures(); void ShutdownAndDestroy(); virtual CPUTResult CreateDXContext(CPUTWindowCreationParams params); // allow user to override DirectX context creation virtual CPUTResult DestroyDXContext(); // allow user to override DirectX context destruction CPUTResult MakeWindow(const cString WindowTitle, int windowWidth, int windowHeight, int windowX, int windowY); CPUTResult CreateAndBindDepthBuffer(int width, int height); void DrawLoadingFrame(); // TODO: Put this somewhere else bool FindMatchingInputSlot(const char *pInputSlotName, const ID3DBlob *pVertexShaderBlob); }; #endif //#ifndef __CPUT_DX11_H__ ================================================ FILE: CPUT/CPUT-DX11.sln ================================================  Microsoft Visual Studio Solution File, Format Version 11.00 # Visual Studio 2010 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CPUT-DX11", "CPUT-DX11.vcxproj", "{8B2DDEDC-A574-4B24-AEC5-03949B5F57BD}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 Debug|x64 = Debug|x64 Release|Win32 = Release|Win32 Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {8B2DDEDC-A574-4B24-AEC5-03949B5F57BD}.Debug|Win32.ActiveCfg = Debug|Win32 {8B2DDEDC-A574-4B24-AEC5-03949B5F57BD}.Debug|Win32.Build.0 = Debug|Win32 {8B2DDEDC-A574-4B24-AEC5-03949B5F57BD}.Debug|x64.ActiveCfg = Debug|x64 {8B2DDEDC-A574-4B24-AEC5-03949B5F57BD}.Debug|x64.Build.0 = Debug|x64 {8B2DDEDC-A574-4B24-AEC5-03949B5F57BD}.Release|Win32.ActiveCfg = Release|Win32 {8B2DDEDC-A574-4B24-AEC5-03949B5F57BD}.Release|Win32.Build.0 = Release|Win32 {8B2DDEDC-A574-4B24-AEC5-03949B5F57BD}.Release|x64.ActiveCfg = Release|x64 {8B2DDEDC-A574-4B24-AEC5-03949B5F57BD}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal ================================================ FILE: CPUT/CPUT-DX11.vcxproj ================================================  Debug Win32 Debug x64 Profile Win32 Profile x64 Release Win32 Release x64 {8B2DDEDC-A574-4B24-AEC5-03949B5F57BD} CPUTDX11 StaticLibrary true Unicode StaticLibrary true Unicode StaticLibrary false true Unicode StaticLibrary false true Unicode StaticLibrary false true Unicode StaticLibrary false true Unicode $(DXSDK_DIR)Include;$(IncludePath); $(LibraryPath);$(DXSDK_DIR)Lib\x64; $(SolutionDir)bin\lib\ $(ProjectName)_64D bin\$(Configuration)\$(Platform)\ $(DXSDK_DIR)Include;$(IncludePath); $(DXSDK_DIR)Include;$(IncludePath); $(LibraryPath);$(LibraryPath);$(DXSDK_DIR)Lib\x64; $(SolutionDir)bin\lib\ $(ProjectName)_64R bin\$(Configuration)\$(Platform)\ $(LibraryPath);$(LibraryPath);$(DXSDK_DIR)Lib\x64; $(SolutionDir)bin\lib\ $(ProjectName)_64R bin\$(Configuration)\$(Platform)\ $(DXSDK_DIR)Include;$(IncludePath); $(LibraryPath);$(DXSDK_DIR)Lib\x86; $(SolutionDir)bin\lib\ $(ProjectName)_32D bin\$(Configuration)\$(Platform)\ $(DXSDK_DIR)Include;$(IncludePath); $(DXSDK_DIR)Include;$(IncludePath); $(LibraryPath);$(DXSDK_DIR)Lib\x86; $(SolutionDir)bin\lib\ $(ProjectName)_32R bin\$(Configuration)\$(Platform)\ $(LibraryPath);$(DXSDK_DIR)Lib\x86; $(SolutionDir)bin\lib\ $(ProjectName)_32R bin\$(Configuration)\$(Platform)\ Level3 Disabled WIN32;_DEBUG;_WINDOWS;_UNICODE;UNICODE;%(PreprocessorDefinitions) true $(IntDir)vc$(PlatformToolsetVersion).pdb Sync true false true $(OutDir)$(TargetName)$(TargetExt) Level3 Disabled WIN32;_DEBUG;_WINDOWS;_UNICODE;UNICODE;%(PreprocessorDefinitions) true $(IntDir)vc$(PlatformToolsetVersion).pdb true false true $(OutDir)$(TargetName)$(TargetExt) Level3 MaxSpeed true true true $(IntDir)vc$(PlatformToolsetVersion).pdb false false true true true $(OutDir)$(TargetName)$(TargetExt) Level3 MaxSpeed true true true $(IntDir)vc$(PlatformToolsetVersion).pdb false false true true true $(OutDir)$(TargetName)$(TargetExt) Level3 Full true true true $(IntDir)vc$(PlatformToolsetVersion).pdb false false true false false true true true true $(OutDir)$(TargetName)$(TargetExt) Level3 MaxSpeed true true true $(IntDir)vc$(PlatformToolsetVersion).pdb false false true true true $(OutDir)$(TargetName)$(TargetExt) ================================================ FILE: CPUT/CPUT-DX11.vcxproj.filters ================================================  {ec1f83ec-dbc4-4148-be0b-3d90f8e36a1b} {3eb25951-0f7f-4d1a-abc3-b1b8abbfa219} {2deb5dac-e9a1-42dc-9767-12d8f3a3ec47} {376a6f7e-4f24-414c-91ec-2c4c17feff31} {5bc7d424-d8fb-484a-9bf3-24461d8f96f6} {31852ab3-f1df-435c-9e12-ba47e47e0b7e} {263c2361-b0f5-40e2-a2f2-5642f0208830} {b112b9da-1a18-4eab-b53b-0b2c29094928} {e9cd7379-7d27-4ddf-b87f-88a608940e77} {1e07919a-0326-4ce5-a15a-b06d16643b14} {ff805aa1-d1b9-4eb4-b408-9599923770fe} Asset Asset Asset Asset GUI GUI Models Models System System System Asset Asset Models GUI GUI GUI GUI Materials Asset\CamerasLights Asset\CamerasLights System Models Materials System System RenderSystems GUI GUI GUI GUI Asset\CamerasLights Materials\Shaders Materials\Shaders Materials\Shaders Materials\Shaders Materials\Shaders Materials\Shaders Materials\Textures Materials\Textures Materials\RenderStates Materials\RenderStates RenderSystems RenderSystems Materials\Shaders Models Materials\Buffers Materials\Buffers Asset Asset Asset Asset Asset Asset GUI GUI GUI GUI Models Models System System System System System Asset Asset Models Models GUI GUI GUI GUI Materials Materials Asset\CamerasLights Asset\CamerasLights System System System RenderSystems GUI GUI GUI GUI System Asset\CamerasLights Materials\Shaders Materials\Shaders Materials\Shaders Materials\Shaders Materials\Shaders Materials\Shaders Materials\Textures Materials\Textures Materials\RenderStates Materials\RenderStates Materials\RenderStates RenderSystems RenderSystems Materials\Shaders Models Models Materials\Buffers Materials\Buffers ================================================ FILE: CPUT/CPUT-DX11_2012.vcxproj ================================================  Debug Win32 Debug x64 Profile Win32 Profile x64 Release Win32 Release x64 {8B2DDEDC-A574-4B24-AEC5-03949B5F57BD} CPUTDX11 CPUT-DX11 StaticLibrary true Unicode v110 StaticLibrary true Unicode v110 StaticLibrary false true Unicode v110 StaticLibrary false true Unicode v110 StaticLibrary false true Unicode v110 StaticLibrary false true Unicode v110 $(IncludePath);$(DXSDK_DIR)Include; $(DXSDK_DIR)Lib\x64;$(LibraryPath); $(SolutionDir)bin\lib\ $(ProjectName)_64D bin\$(Configuration)\$(Platform)\ $(IncludePath);$(DXSDK_DIR)Include; $(IncludePath);$(DXSDK_DIR)Include; $(DXSDK_DIR)Lib\x64;$(LibraryPath); $(SolutionDir)bin\lib\ $(ProjectName)_64R bin\$(Configuration)\$(Platform)\ $(DXSDK_DIR)Lib\x64;$(LibraryPath); $(SolutionDir)bin\lib\ $(ProjectName)_64R bin\$(Configuration)\$(Platform)\ $(IncludePath);$(DXSDK_DIR)Include; $(DXSDK_DIR)Lib\x86;$(LibraryPath); $(SolutionDir)bin\lib\ $(ProjectName)_32D bin\$(Configuration)\$(Platform)\ $(IncludePath);$(DXSDK_DIR)Include; $(IncludePath);$(DXSDK_DIR)Include; $(DXSDK_DIR)Lib\x86;$(LibraryPath); $(SolutionDir)bin\lib\ $(ProjectName)_32R bin\$(Configuration)\$(Platform)\ $(DXSDK_DIR)Lib\x86;$(LibraryPath); $(SolutionDir)bin\lib\ $(ProjectName)_32R bin\$(Configuration)\$(Platform)\ Level3 Disabled WIN32;_DEBUG;_WINDOWS;_UNICODE;UNICODE;%(PreprocessorDefinitions) true $(IntDir)vc$(PlatformToolsetVersion).pdb Sync true false true $(OutDir)$(TargetName)$(TargetExt) Level3 Disabled WIN32;_DEBUG;_WINDOWS;_UNICODE;UNICODE;%(PreprocessorDefinitions) true $(IntDir)vc$(PlatformToolsetVersion).pdb true false true $(OutDir)$(TargetName)$(TargetExt) Level3 MaxSpeed true true true $(IntDir)vc$(PlatformToolsetVersion).pdb false false true true true $(OutDir)$(TargetName)$(TargetExt) Level3 MaxSpeed true true true $(IntDir)vc$(PlatformToolsetVersion).pdb false false true true true $(OutDir)$(TargetName)$(TargetExt) Level3 Full true true true $(IntDir)vc$(PlatformToolsetVersion).pdb false false true false false true true true true $(OutDir)$(TargetName)$(TargetExt) Level3 MaxSpeed true true true $(IntDir)vc$(PlatformToolsetVersion).pdb false false true true true $(OutDir)$(TargetName)$(TargetExt) ================================================ FILE: CPUT/CPUT-DX11_2012.vcxproj.filters ================================================  {ec1f83ec-dbc4-4148-be0b-3d90f8e36a1b} {3eb25951-0f7f-4d1a-abc3-b1b8abbfa219} {2deb5dac-e9a1-42dc-9767-12d8f3a3ec47} {376a6f7e-4f24-414c-91ec-2c4c17feff31} {5bc7d424-d8fb-484a-9bf3-24461d8f96f6} {31852ab3-f1df-435c-9e12-ba47e47e0b7e} {263c2361-b0f5-40e2-a2f2-5642f0208830} {b112b9da-1a18-4eab-b53b-0b2c29094928} {e9cd7379-7d27-4ddf-b87f-88a608940e77} {1e07919a-0326-4ce5-a15a-b06d16643b14} {ff805aa1-d1b9-4eb4-b408-9599923770fe} Asset Asset Asset Asset GUI GUI Models Models System System System Asset Asset Models GUI GUI GUI GUI Materials Asset\CamerasLights Asset\CamerasLights System Models Materials System System RenderSystems GUI GUI GUI GUI Asset\CamerasLights Materials\Shaders Materials\Shaders Materials\Shaders Materials\Shaders Materials\Shaders Materials\Shaders Materials\Textures Materials\Textures Materials\RenderStates Materials\RenderStates RenderSystems RenderSystems Materials\Shaders Models Materials\Buffers Materials\Buffers Asset Asset Asset Asset Asset Asset GUI GUI GUI GUI Models Models System System System System System Asset Asset Models Models GUI GUI GUI GUI Materials Materials Asset\CamerasLights Asset\CamerasLights System System System RenderSystems GUI GUI GUI GUI System Asset\CamerasLights Materials\Shaders Materials\Shaders Materials\Shaders Materials\Shaders Materials\Shaders Materials\Shaders Materials\Textures Materials\Textures Materials\RenderStates Materials\RenderStates Materials\RenderStates RenderSystems RenderSystems Materials\Shaders Models Models Materials\Buffers Materials\Buffers ================================================ FILE: CPUT/resources/controls/atlas/atlas.cmd ================================================ -fbx:atlas.fbx -outputdir:../CPRT/atlas/ ================================================ FILE: CPUT/resources/controls/atlas/atlas.set ================================================ [node 0] type = model name = polySurface1 parent = -1 matrixColumn0 = 1 0 -0 0 matrixColumn1 = 0 1 -0 0 matrixColumn2 = -0 -0 1 -0 matrixColumn3 = 0 0 0 1 BoundingBoxCenter = 0.0527344 0 -0 BoundingBoxHalf = 0.0527344 0 0 meshcount = 1 material0 = lambert1 ================================================ FILE: CPUT/resources/controls/atlas/lambert1.mtl ================================================ shadingmodel = lambert AmbientColor = 0 0 0 AmbientFactor = 1 Bump = 0 0 0 BumpFactor = 1 DiffuseColor = 0.5 0.5 0.5 DiffuseFactor = 0.8 DisplacementColor = 0 0 0 DisplacementFactor = 1 EmissiveColor = 0 0 0 EmissiveFactor = 1 TransparentColor = 0 0 0 TransparencyFactor = 1 VertexShaderFile = vertexShaderDX11 VertexShaderMain = VS VertexShaderProfile = vs_4_0 PixelShaderFile = pixelShaderDX11 PixelShaderMain = PS PixelShaderProfile = ps_4_0 ================================================ FILE: CPUT/resources/shaders/GUIRenderState.rs ================================================ [DepthStencilStateDX11] DepthEnable = false [RasterizerStateDX11] FillMode = D3D11_FILL_SOLID CullMode = D3D11_CULL_BACK FrontCounterClockwise = false DepthBias = 0.0 DepthBiasClamp = 0.0 SlopeScaledDepthBias = 0.0 DepthClipEnable = true ScissorEnable = false MultisampleEnable = false AntialiasedLineEnable = false [RenderTargetBlendStateDX11_1] blendenable = true srcblend = D3D11_BLEND_SRC_ALPHA destblend = D3D11_BLEND_INV_SRC_ALPHA srcblendalpha = D3D11_BLEND_ONE destblendalpha = D3D11_BLEND_ONE blendop = D3D11_BLEND_OP_ADD blendopalpha = D3D11_BLEND_OP_ADD rendertargetwritemask = 15 // D3D11_COLOR_WRITE_ENABLE_ALL [BlendStateDX11] alphatocoverageenable = false independentblendenable = false; [SamplerDX11_1] filter = D3D11_FILTER_MIN_MAG_MIP_POINT AddressU = D3D11_TEXTURE_ADDRESS_CLAMP AddressV = D3D11_TEXTURE_ADDRESS_CLAMP AddressW = D3D11_TEXTURE_ADDRESS_CLAMP MipLODBias = 0 MaxAnisotropy = 0 ComparisonFunc = D3D11_COMPARISON_NEVER BorderColor0 = 0 BorderColor1 = 0 BorderColor2 = 0 BorderColor3 = 0 MinLOD = 0 MaxLOD = 3.402823466e+38f // D3D11_FLOAT32_MAX ================================================ FILE: CPUT/resources/shaders/GUIShaderDX.ps ================================================ //-------------------------------------------------------------------------------------- // Pixel Shader //-------------------------------------------------------------------------------------- Texture2D txDiffuse : register( t0 ); SamplerState samLinear : register( s0 ); struct PS_INPUT { float4 Pos : SV_POSITION; float2 Tex : TEXCOORD; float4 Color: COLOR; }; // alpha blended texture modulated by vertex color float4 PS( PS_INPUT input) : SV_Target { return txDiffuse.Sample( samLinear, input.Tex ) * input.Color; } ================================================ FILE: CPUT/resources/shaders/GUIShaderDX.vs ================================================ //-------------------------------------------------------------------------------------- // Vertex Shader //-------------------------------------------------------------------------------------- Texture2D txDiffuse : register( t0 ); SamplerState samLinear : register( s0 ); struct VS_INPUT { float4 Pos : POSITION; float2 Tex : TEXCOORD; float4 Color: COLOR; }; struct PS_INPUT { float4 Pos : SV_POSITION; float2 Tex : TEXCOORD; float4 Color: COLOR; }; cbuffer ConstantBuffer : register( cb0 ) { matrix projectionMatrix; matrix modelMatrix; }; PS_INPUT VS( VS_INPUT input ) { PS_INPUT output = (PS_INPUT)0; input.Pos.w = 1.0f; output.Pos = mul( input.Pos, modelMatrix ); output.Pos = mul( output.Pos, projectionMatrix ); output.Tex = input.Tex; output.Color = input.Color; return output; } ================================================ FILE: Default_Config.txt ================================================ RawDEMDataFile = media\Terrain\HeightMap.tif MaterialMaskFile = media\Terrain\Mask.png TexturingMode = MaterialMaskNM ElevationSamplingInterval = 32 ScalingFactor = 10 RingDimension = 65 NumRings = 15 ColOffset = 1356 RowOffset = 924 TileTexture0 = media\tiles\gravel_DM.dds TileTexture1 = media\tiles\grass_DM.dds TileTexture2 = media\tiles\cliff_DM.dds TileTexture3 = media\tiles\snow_DM.dds TileTexture4 = media\tiles\grassDark_DM.dds TileNormalMap0 = media\tiles\gravel_NM.dds TileNormalMap1 = media\tiles\grass_NM.dds TileNormalMap2 = media\tiles\cliff_NM.dds TileNormalMap3 = media\tiles\Snow_NM.jpg TileNormalMap4 = media\tiles\grass_NM.dds TilingScale0 = 200 TilingScale1 = 500 TilingScale2 = 800 TilingScale3 = 80 TilingScale4 = 80 AnimateSun = false ================================================ FILE: LightSctrPostProcess.cpp ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #include "StdAfx.h" #include "LightSctrPostProcess.h" #include #include #include #include "ShaderMacroHelper.h" #define _USE_MATH_DEFINES #include #ifndef V #define V(x) { hr = (x); assert( SUCCEEDED(hr) ); } #endif #ifndef V_RETURN #define V_RETURN(x) { hr = (x); assert( SUCCEEDED(hr) ); if( FAILED(hr) ) { return hr; } } #endif CLightSctrPostProcess :: CLightSctrPostProcess() : m_uiSampleRefinementCSThreadGroupSize(0), // Using small group size is inefficient because a lot of SIMD lanes become idle m_uiSampleRefinementCSMinimumThreadGroupSize(128),// Must be greater than 32 m_fTurbidity(1.02f), m_strEffectPath( L"fx\\LightScattering.fx" ), m_bUseCombinedMinMaxTexture(false) { ComputeScatteringCoefficients(); } CLightSctrPostProcess :: ~CLightSctrPostProcess() { } HRESULT CLightSctrPostProcess :: OnCreateDevice(ID3D11Device* in_pd3dDevice, ID3D11DeviceContext *in_pd3dDeviceContext) { HRESULT hr; // Create depth stencil states D3D11_DEPTH_STENCIL_DESC EnableDepthTestDSDesc; ZeroMemory(&EnableDepthTestDSDesc, sizeof(EnableDepthTestDSDesc)); EnableDepthTestDSDesc.DepthEnable = TRUE; EnableDepthTestDSDesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ZERO; EnableDepthTestDSDesc.DepthFunc = D3D11_COMPARISON_EQUAL; V_RETURN( in_pd3dDevice->CreateDepthStencilState( &EnableDepthTestDSDesc, &m_pEnableDepthCmpEqDS) ); // Disable depth testing D3D11_DEPTH_STENCIL_DESC DisableDepthTestDSDesc; ZeroMemory(&DisableDepthTestDSDesc, sizeof(DisableDepthTestDSDesc)); DisableDepthTestDSDesc.DepthEnable = FALSE; DisableDepthTestDSDesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ZERO; V_RETURN( in_pd3dDevice->CreateDepthStencilState( &DisableDepthTestDSDesc, &m_pDisableDepthTestDS) ); // Disable depth testing and always increment stencil value // This depth stencil state is used to mark samples which will undergo further processing // Pixel shader discards pixels which should not be further processed, thus keeping the // stencil value untouched // For instance, pixel shader performing epipolar coordinates generation discards all // sampes, whoose coordinates are outside the screen [-1,1]x[-1,1] area D3D11_DEPTH_STENCIL_DESC DisbaleDepthIncrStencilDSSDesc = DisableDepthTestDSDesc; DisbaleDepthIncrStencilDSSDesc.StencilEnable = TRUE; DisbaleDepthIncrStencilDSSDesc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS; DisbaleDepthIncrStencilDSSDesc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_INCR; DisbaleDepthIncrStencilDSSDesc.FrontFace.StencilFailOp = D3D11_STENCIL_OP_KEEP; DisbaleDepthIncrStencilDSSDesc.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_KEEP; DisbaleDepthIncrStencilDSSDesc.BackFace.StencilFunc = D3D11_COMPARISON_ALWAYS; DisbaleDepthIncrStencilDSSDesc.BackFace.StencilPassOp = D3D11_STENCIL_OP_INCR; DisbaleDepthIncrStencilDSSDesc.BackFace.StencilFailOp = D3D11_STENCIL_OP_KEEP; DisbaleDepthIncrStencilDSSDesc.BackFace.StencilDepthFailOp = D3D11_STENCIL_OP_KEEP; DisbaleDepthIncrStencilDSSDesc.StencilReadMask = D3D11_DEFAULT_STENCIL_READ_MASK; DisbaleDepthIncrStencilDSSDesc.StencilWriteMask = D3D11_DEFAULT_STENCIL_WRITE_MASK; V_RETURN( in_pd3dDevice->CreateDepthStencilState( &DisbaleDepthIncrStencilDSSDesc, &m_pDisableDepthTestIncrStencilDS) ); // Disable depth testing, stencil testing function equal, increment stencil // This state is used to process only these pixels that were marked at the previous pass // All pixels whith different stencil value are discarded from further processing as well // as some pixels can also be discarded during the draw call // For instance, pixel shader marking ray marching samples processes only these pixels which are inside // the screen. It also discards all but these samples which are interpolated from themselves D3D11_DEPTH_STENCIL_DESC DisbaleDepthStencilEqualIncrStencilDSSDesc = DisbaleDepthIncrStencilDSSDesc; DisbaleDepthStencilEqualIncrStencilDSSDesc.FrontFace.StencilFunc = D3D11_COMPARISON_EQUAL; DisbaleDepthStencilEqualIncrStencilDSSDesc.BackFace.StencilFunc = D3D11_COMPARISON_EQUAL; V_RETURN( in_pd3dDevice->CreateDepthStencilState( &DisbaleDepthStencilEqualIncrStencilDSSDesc, &m_pNoDepth_StEqual_IncrStencilDS) ); D3D11_DEPTH_STENCIL_DESC DisbaleDepthStencilEqualKeepStencilDSSDesc = DisbaleDepthStencilEqualIncrStencilDSSDesc; DisbaleDepthStencilEqualKeepStencilDSSDesc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP; DisbaleDepthStencilEqualKeepStencilDSSDesc.BackFace.StencilPassOp = D3D11_STENCIL_OP_KEEP; V_RETURN( in_pd3dDevice->CreateDepthStencilState( &DisbaleDepthStencilEqualKeepStencilDSSDesc, &m_pNoDepth_StEqual_KeepStencilDS) ); // Create rasterizer state D3D11_RASTERIZER_DESC SolidFillNoCullRSDesc; ZeroMemory(&SolidFillNoCullRSDesc, sizeof(SolidFillNoCullRSDesc)); SolidFillNoCullRSDesc.FillMode = D3D11_FILL_SOLID; SolidFillNoCullRSDesc.CullMode = D3D11_CULL_NONE; V_RETURN( in_pd3dDevice->CreateRasterizerState( &SolidFillNoCullRSDesc, &m_pSolidFillNoCullRS) ); // Create default blend state D3D11_BLEND_DESC DefaultBlendStateDesc; ZeroMemory(&DefaultBlendStateDesc, sizeof(DefaultBlendStateDesc)); DefaultBlendStateDesc.IndependentBlendEnable = FALSE; for(int i=0; i< _countof(DefaultBlendStateDesc.RenderTarget); i++) DefaultBlendStateDesc.RenderTarget[i].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; V_RETURN( in_pd3dDevice->CreateBlendState( &DefaultBlendStateDesc, &m_pDefaultBS) ); D3D11_BLEND_DESC AdditiveBlendStateDesc; ZeroMemory(&AdditiveBlendStateDesc, sizeof(AdditiveBlendStateDesc)); AdditiveBlendStateDesc.IndependentBlendEnable = FALSE; for(int i=0; i< _countof(AdditiveBlendStateDesc.RenderTarget); i++) AdditiveBlendStateDesc.RenderTarget[i].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; AdditiveBlendStateDesc.RenderTarget[0].BlendEnable = TRUE; AdditiveBlendStateDesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD; AdditiveBlendStateDesc.RenderTarget[0].BlendOpAlpha= D3D11_BLEND_OP_ADD; AdditiveBlendStateDesc.RenderTarget[0].DestBlend = D3D11_BLEND_ONE; AdditiveBlendStateDesc.RenderTarget[0].DestBlendAlpha= D3D11_BLEND_ONE; AdditiveBlendStateDesc.RenderTarget[0].SrcBlend = D3D11_BLEND_ONE; AdditiveBlendStateDesc.RenderTarget[0].SrcBlendAlpha= D3D11_BLEND_ONE; V_RETURN( in_pd3dDevice->CreateBlendState( &AdditiveBlendStateDesc, &m_pAdditiveBlendBS) ); D3D11_BLEND_DESC AlphaBlendStateDesc = AdditiveBlendStateDesc; AlphaBlendStateDesc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA; AlphaBlendStateDesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_SRC_ALPHA; AlphaBlendStateDesc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA; AlphaBlendStateDesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA; V_RETURN( in_pd3dDevice->CreateBlendState( &AlphaBlendStateDesc, &m_pAlphaBlendBS) ); // Create samplers D3D11_SAMPLER_DESC SamLinearBorder0Desc = { D3D11_FILTER_MIN_MAG_MIP_LINEAR, D3D11_TEXTURE_ADDRESS_BORDER, D3D11_TEXTURE_ADDRESS_BORDER, D3D11_TEXTURE_ADDRESS_BORDER, 0, //FLOAT MipLODBias; 0, //UINT MaxAnisotropy; D3D11_COMPARISON_NEVER, // D3D11_COMPARISON_FUNC ComparisonFunc; {0.f, 0.f, 0.f, 0.f}, //FLOAT BorderColor[ 4 ]; -FLT_MAX, //FLOAT MinLOD; +FLT_MAX //FLOAT MaxLOD; }; V_RETURN( in_pd3dDevice->CreateSamplerState( &SamLinearBorder0Desc, &m_psamLinearBorder0) ); D3D11_SAMPLER_DESC SamLinearClampDesc = { D3D11_FILTER_MIN_MAG_MIP_LINEAR, D3D11_TEXTURE_ADDRESS_CLAMP, D3D11_TEXTURE_ADDRESS_CLAMP, D3D11_TEXTURE_ADDRESS_CLAMP, 0, //FLOAT MipLODBias; 0, //UINT MaxAnisotropy; D3D11_COMPARISON_NEVER, // D3D11_COMPARISON_FUNC ComparisonFunc; {0.f, 0.f, 0.f, 0.f}, //FLOAT BorderColor[ 4 ]; -FLT_MAX, //FLOAT MinLOD; +FLT_MAX //FLOAT MaxLOD; }; V_RETURN( in_pd3dDevice->CreateSamplerState( &SamLinearClampDesc, &m_psamLinearClamp) ); D3D11_SAMPLER_DESC SamPointClampDesc = SamLinearClampDesc; SamPointClampDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT; V_RETURN( in_pd3dDevice->CreateSamplerState( &SamPointClampDesc, &m_psamPointClamp) ); D3D11_SAMPLER_DESC SamComparisonDesc = { D3D11_FILTER_COMPARISON_MIN_MAG_LINEAR_MIP_POINT, D3D11_TEXTURE_ADDRESS_BORDER, D3D11_TEXTURE_ADDRESS_BORDER, D3D11_TEXTURE_ADDRESS_BORDER, 0, //FLOAT MipLODBias; 0, //UINT MaxAnisotropy; D3D11_COMPARISON_GREATER, // D3D11_COMPARISON_FUNC ComparisonFunc; {0.f, 0.f, 0.f, 0.f}, //FLOAT BorderColor[ 4 ]; -FLT_MAX, //FLOAT MinLOD; +FLT_MAX //FLOAT MaxLOD; }; V_RETURN( in_pd3dDevice->CreateSamplerState( &SamComparisonDesc, &m_psamComparison) ); // Create constant buffers D3D11_BUFFER_DESC CBDesc = { sizeof(SCameraAttribs), D3D11_USAGE_DYNAMIC, D3D11_BIND_CONSTANT_BUFFER, D3D11_CPU_ACCESS_WRITE, //UINT CPUAccessFlags 0, //UINT MiscFlags; 0, //UINT StructureByteStride; }; V_RETURN( in_pd3dDevice->CreateBuffer( &CBDesc, NULL, &m_pcbCameraAttribs) ); //CBDesc.ByteWidth = sizeof(SLightAttribs); //V_RETURN( in_pd3dDevice->CreateBuffer( &CBDesc, NULL, &m_pcbLightAttribs) ); CBDesc.ByteWidth = sizeof(SPostProcessingAttribs); V_RETURN( in_pd3dDevice->CreateBuffer( &CBDesc, NULL, &m_pcbPostProcessingAttribs) ); CBDesc.ByteWidth = sizeof(SMiscDynamicParams); V_RETURN( in_pd3dDevice->CreateBuffer( &CBDesc, NULL, &m_pcbMiscParams) ); CBDesc.ByteWidth = sizeof(SAirScatteringAttribs); CBDesc.Usage = D3D11_USAGE_DEFAULT; CBDesc.CPUAccessFlags = 0; D3D11_SUBRESOURCE_DATA InitData = { &m_MediaParams, 0, // UINT SysMemPitch 0 // UINT SysMemSlicePitch }; V_RETURN( in_pd3dDevice->CreateBuffer( &CBDesc, &InitData, &m_pcbMediaAttribs) ); CRenderTechnique GenerateScreenSizeQuadTech; GenerateScreenSizeQuadTech.SetDeviceAndContext( in_pd3dDevice, in_pd3dDeviceContext ); V( GenerateScreenSizeQuadTech.CreateVertexShaderFromFile(m_strEffectPath, "GenerateScreenSizeQuadVS", NULL ) ); m_pGenerateScreenSizeQuadVS = GenerateScreenSizeQuadTech.GetVS(); CreatePrecomputedOpticalDepthTexture(in_pd3dDevice, in_pd3dDeviceContext); return S_OK; } void CLightSctrPostProcess :: OnDestroyDevice() { m_ptex2DSliceEndpointsSRV.Release(); m_ptex2DSliceEndpointsRTV.Release(); m_ptex2DCoordinateTextureSRV.Release(); m_ptex2DCoordinateTextureRTV.Release(); m_ptex2DEpipolarImageDSV.Release(); m_ptex2DInterpolationSourcesSRV.Release(); m_ptex2DInterpolationSourcesUAV.Release(); m_ptex2DEpipolarCamSpaceZSRV.Release(); m_ptex2DEpipolarCamSpaceZRTV.Release(); m_ptex2DEpipolarInscatteringSRV.Release(); m_ptex2DEpipolarInscatteringRTV.Release(); m_ptex2DEpipolarExtinctionSRV.Release(); m_ptex2DEpipolarExtinctionRTV.Release(); m_ptex2DInitialScatteredLightSRV.Release(); m_ptex2DInitialScatteredLightRTV.Release(); m_ptex2DScreenSizeDSV.Release(); m_ptex2DCameraSpaceZRTV.Release(); m_ptex2DCameraSpaceZSRV.Release(); for(int i=0; i < _countof(m_ptex2DMinMaxShadowMapSRV); ++i) m_ptex2DMinMaxShadowMapSRV[i].Release(); for(int i=0; i < _countof(m_ptex2DMinMaxShadowMapRTV); ++i) m_ptex2DMinMaxShadowMapRTV[i].Release(); m_ptex2DSliceUVDirAndOriginSRV.Release(); m_ptex2DSliceUVDirAndOriginRTV.Release(); m_ptex2DOccludedNetDensityToAtmTopSRV.Release(); m_ptex2DOccludedNetDensityToAtmTopRTV.Release(); m_ptex3DSingleScatteringSRV.Release(); m_ptex3DHighOrderScatteringSRV.Release(); m_ptex3DMultipleScatteringSRV.Release(); m_ptex2DLowResLuminanceSRV.Release(); m_ptex2DLowResLuminanceRTV.Release(); m_ptex2DAverageLuminanceRTV.Release(); m_ptex2DAverageLuminanceSRV.Release(); m_ptex2DAmbientSkyLightRTV.Release(); m_ptex2DAmbientSkyLightSRV.Release(); m_ptex2DSphereRandomSamplingSRV.Release(); m_psamLinearClamp.Release(); m_psamLinearBorder0.Release(); m_psamComparison.Release(); m_psamPointClamp.Release(); m_ReconstrCamSpaceZTech.Release(); m_RendedSliceEndpointsTech.Release(); m_RendedCoordTexTech.Release(); m_RefineSampleLocationsTech.Release(); m_RenderCoarseUnshadowedInsctrTech.Release(); m_MarkRayMarchingSamplesInStencilTech.Release(); m_RenderSliceUVDirInSMTech.Release(); m_InitializeMinMaxShadowMapTech.Release(); m_ComputeMinMaxSMLevelTech.Release(); m_DoRayMarchTech[0].Release(); m_DoRayMarchTech[1].Release(); m_InterpolateIrradianceTech.Release(); m_UnwarpEpipolarSctrImgTech.Release(); m_UnwarpAndRenderLuminanceTech.Release(); m_UpdateAverageLuminanceTech.Release(); for(size_t i=0; i<_countof(m_FixInsctrAtDepthBreaksTech); ++i) m_FixInsctrAtDepthBreaksTech[i].Release(); m_RenderSampleLocationsTech.Release(); m_RenderSunTech.Release(); m_PrecomputeSingleSctrTech.Release(); m_ComputeSctrRadianceTech.Release(); m_ComputeScatteringOrderTech.Release(); m_AddScatteringOrderTech.Release(); m_pGenerateScreenSizeQuadVS.Release(); m_pEnableDepthCmpEqDS.Release(); m_pDisableDepthTestDS.Release(); m_pDisableDepthTestIncrStencilDS.Release(); m_pNoDepth_StEqual_IncrStencilDS.Release(); m_pNoDepth_StEqual_KeepStencilDS.Release(); m_pSolidFillNoCullRS.Release(); m_pDefaultBS.Release(); m_pAdditiveBlendBS.Release(); m_pAlphaBlendBS.Release(); m_pcbCameraAttribs.Release(); //m_pcbLightAttribs.Release(); m_pcbPostProcessingAttribs.Release(); m_pcbMediaAttribs.Release(); m_pcbMiscParams.Release(); } HRESULT CLightSctrPostProcess :: OnResizedSwapChain(ID3D11Device* pd3dDevice, UINT uiBackBufferWidth, UINT uiBackBufferHeight) { m_uiBackBufferWidth = uiBackBufferWidth; m_uiBackBufferHeight = uiBackBufferHeight; D3D11_TEXTURE2D_DESC ScreenSizeDepthStencilTexDesc = { uiBackBufferWidth, //UINT Width; uiBackBufferHeight, //UINT Height; 1, //UINT MipLevels; 1, //UINT ArraySize; DXGI_FORMAT_D24_UNORM_S8_UINT, //DXGI_FORMAT Format; {1,0}, //DXGI_SAMPLE_DESC SampleDesc; D3D11_USAGE_DEFAULT, //D3D11_USAGE Usage; D3D11_BIND_DEPTH_STENCIL, //UINT BindFlags; 0, //UINT CPUAccessFlags; 0, //UINT MiscFlags; }; m_ptex2DScreenSizeDSV.Release(); CComPtr ptex2DScreenSizeDepthStencil; // Create 2-D texture, shader resource and target view buffers on the device HRESULT hr; V_RETURN( pd3dDevice->CreateTexture2D( &ScreenSizeDepthStencilTexDesc, NULL, &ptex2DScreenSizeDepthStencil) ); V_RETURN( pd3dDevice->CreateDepthStencilView( ptex2DScreenSizeDepthStencil, NULL, &m_ptex2DScreenSizeDSV) ); m_ptex2DCameraSpaceZRTV.Release(); m_ptex2DCameraSpaceZSRV.Release(); D3D11_TEXTURE2D_DESC CamSpaceZTexDesc = ScreenSizeDepthStencilTexDesc; CamSpaceZTexDesc.Format = DXGI_FORMAT_R32_FLOAT; CamSpaceZTexDesc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE; CComPtr ptex2DCamSpaceZ; V_RETURN( pd3dDevice->CreateTexture2D( &CamSpaceZTexDesc, NULL, &ptex2DCamSpaceZ) ); V_RETURN( pd3dDevice->CreateShaderResourceView( ptex2DCamSpaceZ, NULL, &m_ptex2DCameraSpaceZSRV) ); V_RETURN( pd3dDevice->CreateRenderTargetView( ptex2DCamSpaceZ, NULL, &m_ptex2DCameraSpaceZRTV) ); m_RendedSliceEndpointsTech.Release(); m_RendedCoordTexTech.Release(); m_RenderSliceUVDirInSMTech.Release(); m_RenderSampleLocationsTech.Release(); m_UnwarpEpipolarSctrImgTech.Release(); m_UnwarpAndRenderLuminanceTech.Release(); return S_OK; } static void UnbindResources(ID3D11DeviceContext *pDeviceCtx) { ID3D11ShaderResourceView *pDummySRVs[12]={NULL}; ID3D11UnorderedAccessView *pDummyUAVs[8]={NULL}; pDeviceCtx->PSSetShaderResources(0, _countof(pDummySRVs), pDummySRVs); pDeviceCtx->VSSetShaderResources(0, _countof(pDummySRVs), pDummySRVs); pDeviceCtx->GSSetShaderResources(0, _countof(pDummySRVs), pDummySRVs); pDeviceCtx->CSSetShaderResources(0, _countof(pDummySRVs), pDummySRVs); pDeviceCtx->CSSetUnorderedAccessViews(0, _countof(pDummyUAVs), pDummyUAVs, NULL); } static void RenderQuad(ID3D11DeviceContext *pd3dDeviceCtx, CRenderTechnique &State, int iWidth = 0, int iHeight = 0, int iTopLeftX = 0, int iTopLeftY = 0, int iNumInstances = 1) { // Initialize the viewport if( !iWidth && !iHeight ) { assert( iTopLeftX == 0 && iTopLeftY == 0 ); CComPtr pRTV; pd3dDeviceCtx->OMGetRenderTargets(1, &pRTV, NULL); CComPtr pDstTex; pRTV->GetResource( &pDstTex ); D3D11_TEXTURE2D_DESC DstTexDesc; CComQIPtr(pDstTex)->GetDesc( &DstTexDesc ); iWidth = DstTexDesc.Width; iHeight = DstTexDesc.Height; } D3D11_VIEWPORT NewViewPort; NewViewPort.TopLeftX = static_cast( iTopLeftX ); NewViewPort.TopLeftY = static_cast( iTopLeftY ); NewViewPort.Width = static_cast( iWidth ); NewViewPort.Height = static_cast( iHeight ); NewViewPort.MinDepth = 0; NewViewPort.MaxDepth = 1; // Set the viewport pd3dDeviceCtx->RSSetViewports(1, &NewViewPort); UINT offset[1] = {0}; UINT stride[1] = {0}; ID3D11Buffer *ppBuffers[1] = {0}; pd3dDeviceCtx->IASetVertexBuffers(0, 1, ppBuffers, stride, offset); // There is no input-layout object and the primitive topology is triangle strip pd3dDeviceCtx->IASetInputLayout(NULL); pd3dDeviceCtx->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); State.Apply(); if( iNumInstances == 1 ) { // Draw 4 vertices (two triangles ) pd3dDeviceCtx->Draw(4, 0); } else { // Draw 4 vertices (two triangles ) x number of instances pd3dDeviceCtx->DrawInstanced(4, iNumInstances, 0, 0); } // Unbind resources UnbindResources( pd3dDeviceCtx ); } void CLightSctrPostProcess :: DefineMacros(class CD3DShaderMacroHelper &Macros) { Macros.AddShaderMacro("NUM_EPIPOLAR_SLICES", m_PostProcessingAttribs.m_uiNumEpipolarSlices); Macros.AddShaderMacro("MAX_SAMPLES_IN_SLICE", m_PostProcessingAttribs.m_uiMaxSamplesInSlice); Macros.AddShaderMacro("OPTIMIZE_SAMPLE_LOCATIONS", m_PostProcessingAttribs.m_bOptimizeSampleLocations); Macros.AddShaderMacro("USE_COMBINED_MIN_MAX_TEXTURE", m_bUseCombinedMinMaxTexture ); Macros.AddShaderMacro("EXTINCTION_EVAL_MODE", m_PostProcessingAttribs.m_uiExtinctionEvalMode ); Macros.AddShaderMacro("ENABLE_LIGHT_SHAFTS", m_PostProcessingAttribs.m_bEnableLightShafts); Macros.AddShaderMacro("MULTIPLE_SCATTERING_MODE", m_PostProcessingAttribs.m_uiMultipleScatteringMode); Macros.AddShaderMacro("SINGLE_SCATTERING_MODE", m_PostProcessingAttribs.m_uiSingleScatteringMode); { std::stringstream ss; ss<<"float2("< ptex2DOccludedNetDensityToAtmTop; V_RETURN( in_pd3dDevice->CreateTexture2D( &NetDensityToAtmTopTexDesc, NULL, &ptex2DOccludedNetDensityToAtmTop) ); V_RETURN( in_pd3dDevice->CreateShaderResourceView( ptex2DOccludedNetDensityToAtmTop, NULL, &m_ptex2DOccludedNetDensityToAtmTopSRV) ); V_RETURN( in_pd3dDevice->CreateRenderTargetView( ptex2DOccludedNetDensityToAtmTop, NULL, &m_ptex2DOccludedNetDensityToAtmTopRTV) ); ID3D11RenderTargetView *pRTVs[] = { m_ptex2DOccludedNetDensityToAtmTopRTV }; in_pd3dDeviceContext->OMSetRenderTargets(_countof(pRTVs), pRTVs, NULL); ID3D11Buffer *pCBs[] = {m_pcbMediaAttribs}; in_pd3dDeviceContext->PSSetConstantBuffers(1, _countof(pCBs), pCBs); RenderQuad(in_pd3dDeviceContext, PrecomputeNetDensityToAtmTopTech); in_pd3dDeviceContext->OMSetRenderTargets(0, NULL, NULL); return S_OK; } void CLightSctrPostProcess :: CreateRandomSphereSamplingTexture(ID3D11Device *pDevice) { D3D11_TEXTURE2D_DESC RandomSphereSamplingTexDesc = { sm_iNumRandomSamplesOnSphere, //UINT Width; 1, //UINT Height; 1, //UINT MipLevels; 1, //UINT ArraySize; DXGI_FORMAT_R32G32B32A32_FLOAT, //DXGI_FORMAT Format; {1,0}, //DXGI_SAMPLE_DESC SampleDesc; D3D11_USAGE_IMMUTABLE, //D3D11_USAGE Usage; D3D11_BIND_SHADER_RESOURCE, //UINT BindFlags; 0, //UINT CPUAccessFlags; 0, //UINT MiscFlags; }; std::vector SphereSampling(sm_iNumRandomSamplesOnSphere); for(int iSample = 0; iSample < sm_iNumRandomSamplesOnSphere; ++iSample) { D3DXVECTOR4 &f4Sample = SphereSampling[iSample]; f4Sample.z = ((float)rand()/(float)RAND_MAX) * 2.f - 1.f; float t = ((float)rand()/(float)RAND_MAX) * 2.f * PI; float r = sqrt( max(1 - f4Sample.z*f4Sample.z, 0.f) ); f4Sample.x = r * cos(t); f4Sample.y = r * sin(t); f4Sample.w = 0; } D3D11_SUBRESOURCE_DATA InitData = { &SphereSampling[0], sm_iNumRandomSamplesOnSphere*sizeof(D3DXVECTOR4), // UINT SysMemPitch 0 // UINT SysMemSlicePitch }; HRESULT hr; CComPtr ptex2DSphereRandomSampling; V( pDevice->CreateTexture2D( &RandomSphereSamplingTexDesc, &InitData, &ptex2DSphereRandomSampling) ); V( pDevice->CreateShaderResourceView( ptex2DSphereRandomSampling, NULL, &m_ptex2DSphereRandomSamplingSRV) ); } HRESULT CLightSctrPostProcess :: CreatePrecomputedScatteringLUT(ID3D11Device *pDevice, ID3D11DeviceContext *pContext) { HRESULT hr; if( !m_PrecomputeSingleSctrTech.IsValid() ) { CD3DShaderMacroHelper Macros; DefineMacros(Macros); Macros.Finalize(); m_PrecomputeSingleSctrTech.SetDeviceAndContext(pDevice, pContext); m_PrecomputeSingleSctrTech.CreatePixelShaderFromFile( m_strEffectPath, "PrecomputeSingleScatteringPS", Macros ); m_PrecomputeSingleSctrTech.SetVS( m_pGenerateScreenSizeQuadVS ); m_PrecomputeSingleSctrTech.SetDS( m_pDisableDepthTestDS ); m_PrecomputeSingleSctrTech.SetRS( m_pSolidFillNoCullRS ); m_PrecomputeSingleSctrTech.SetBS( m_pDefaultBS ); } if( !m_ComputeSctrRadianceTech.IsValid() ) { CD3DShaderMacroHelper Macros; DefineMacros(Macros); Macros.AddShaderMacro( "NUM_RANDOM_SPHERE_SAMPLES", sm_iNumRandomSamplesOnSphere ); Macros.Finalize(); m_ComputeSctrRadianceTech.SetDeviceAndContext(pDevice, pContext); m_ComputeSctrRadianceTech.CreatePixelShaderFromFile( m_strEffectPath, "ComputeSctrRadiancePS", Macros ); m_ComputeSctrRadianceTech.SetVS( m_pGenerateScreenSizeQuadVS ); m_ComputeSctrRadianceTech.SetDS( m_pDisableDepthTestDS ); m_ComputeSctrRadianceTech.SetRS( m_pSolidFillNoCullRS ); m_ComputeSctrRadianceTech.SetBS( m_pDefaultBS ); } if( !m_ComputeScatteringOrderTech.IsValid() ) { CD3DShaderMacroHelper Macros; DefineMacros(Macros); Macros.Finalize(); m_ComputeScatteringOrderTech.SetDeviceAndContext(pDevice, pContext); m_ComputeScatteringOrderTech.CreatePixelShaderFromFile( m_strEffectPath, "ComputeScatteringOrderPS", Macros ); m_ComputeScatteringOrderTech.SetVS( m_pGenerateScreenSizeQuadVS ); m_ComputeScatteringOrderTech.SetDS( m_pDisableDepthTestDS ); m_ComputeScatteringOrderTech.SetRS( m_pSolidFillNoCullRS ); m_ComputeScatteringOrderTech.SetBS( m_pDefaultBS ); } if( !m_AddScatteringOrderTech.IsValid() ) { CD3DShaderMacroHelper Macros; DefineMacros(Macros); Macros.Finalize(); m_AddScatteringOrderTech.SetDeviceAndContext(pDevice, pContext); m_AddScatteringOrderTech.CreatePixelShaderFromFile( m_strEffectPath, "AddScatteringOrderPS", Macros ); m_AddScatteringOrderTech.SetVS( m_pGenerateScreenSizeQuadVS ); m_AddScatteringOrderTech.SetDS( m_pDisableDepthTestDS ); m_AddScatteringOrderTech.SetRS( m_pSolidFillNoCullRS ); m_AddScatteringOrderTech.SetBS( m_pAdditiveBlendBS ); } if( !m_ptex2DSphereRandomSamplingSRV ) CreateRandomSphereSamplingTexture(pDevice); m_ptex3DSingleScatteringSRV.Release(); m_ptex3DHighOrderScatteringSRV.Release(); m_ptex3DMultipleScatteringSRV.Release(); D3D11_TEXTURE3D_DESC PrecomputedSctrTexDesc = { sm_iPrecomputedSctrUDim, //UINT Width; sm_iPrecomputedSctrVDim, //UINT Height; sm_iPrecomputedSctrWDim * sm_iPrecomputedSctrQDim, //UINT Depth; 1, //UINT MipLevels; DXGI_FORMAT_R16G16B16A16_FLOAT,//DXGI_FORMAT Format; D3D11_USAGE_DEFAULT, //D3D11_USAGE Usage; D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE,//UINT BindFlags; 0,//UINT CPUAccessFlags; 0//UINT MiscFlags; }; CComPtr ptex3DSingleSctr, ptex3DHighOrderSctr, ptex3DMultipleSctr; V_RETURN(pDevice->CreateTexture3D(&PrecomputedSctrTexDesc, NULL, &ptex3DSingleSctr)); V_RETURN(pDevice->CreateShaderResourceView(ptex3DSingleSctr, NULL, &m_ptex3DSingleScatteringSRV)); V_RETURN(pDevice->CreateTexture3D(&PrecomputedSctrTexDesc, NULL, &ptex3DHighOrderSctr)); V_RETURN(pDevice->CreateShaderResourceView(ptex3DHighOrderSctr, NULL, &m_ptex3DHighOrderScatteringSRV)); V_RETURN(pDevice->CreateTexture3D(&PrecomputedSctrTexDesc, NULL, &ptex3DMultipleSctr)); V_RETURN(pDevice->CreateShaderResourceView(ptex3DMultipleSctr, NULL, &m_ptex3DMultipleScatteringSRV)); std::vector< CComPtr > ptex3DHighOrderSctrRTVs(PrecomputedSctrTexDesc.Depth); std::vector< CComPtr > ptex3DMultipleSctrRTVs(PrecomputedSctrTexDesc.Depth); // Precompute single scattering for(UINT uiDepthSlice = 0; uiDepthSlice < PrecomputedSctrTexDesc.Depth; ++uiDepthSlice) { D3D11_RENDER_TARGET_VIEW_DESC CurrSliceRTVDesc; CurrSliceRTVDesc.Format = PrecomputedSctrTexDesc.Format; CurrSliceRTVDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE3D; CurrSliceRTVDesc.Texture3D.MipSlice = 0; CurrSliceRTVDesc.Texture3D.FirstWSlice = uiDepthSlice; CurrSliceRTVDesc.Texture3D.WSize = 1; CComPtr ptex3DCurrDepthSliceRTV; V_RETURN(pDevice->CreateRenderTargetView(ptex3DSingleSctr, &CurrSliceRTVDesc, &ptex3DCurrDepthSliceRTV)); V_RETURN(pDevice->CreateRenderTargetView(ptex3DHighOrderSctr, &CurrSliceRTVDesc, &ptex3DHighOrderSctrRTVs[uiDepthSlice])); float Zero[] = {0.f, 0.f, 0.f, 0.f}; pContext->ClearRenderTargetView(ptex3DHighOrderSctrRTVs[uiDepthSlice].p, Zero); V_RETURN(pDevice->CreateRenderTargetView(ptex3DMultipleSctr, &CurrSliceRTVDesc, &ptex3DMultipleSctrRTVs[uiDepthSlice])); pContext->OMSetRenderTargets(1, &ptex3DCurrDepthSliceRTV.p, NULL); ID3D11ShaderResourceView *pSRVs[] = { m_ptex2DOccludedNetDensityToAtmTopSRV // Texture2D g_tex2DOccludedNetDensityToAtmTop : register( t5 ); }; pContext->PSSetShaderResources(5, _countof(pSRVs), pSRVs); // Set sun zenith and sun view angles SMiscDynamicParams MiscDynamicParams = {NULL}; UINT uiW = uiDepthSlice % sm_iPrecomputedSctrWDim; UINT uiQ = uiDepthSlice / sm_iPrecomputedSctrWDim; MiscDynamicParams.f2WQ.x = ((float)uiW + 0.5f) / (float)sm_iPrecomputedSctrWDim; assert(0 < MiscDynamicParams.f2WQ.x && MiscDynamicParams.f2WQ.x < 1); MiscDynamicParams.f2WQ.y = ((float)uiQ + 0.5f) / (float)sm_iPrecomputedSctrQDim; assert(0 < MiscDynamicParams.f2WQ.y && MiscDynamicParams.f2WQ.y < 1); UpdateConstantBuffer(pContext, m_pcbMiscParams, &MiscDynamicParams, sizeof(MiscDynamicParams)); //cbuffer cbMiscDynamicParams : register( b4 ) pContext->PSSetConstantBuffers(4, 1, &m_pcbMiscParams.p); RenderQuad( pContext, m_PrecomputeSingleSctrTech, PrecomputedSctrTexDesc.Width, PrecomputedSctrTexDesc.Height ); } // Precompute multiple scattering // We need higher precision to store intermediate data PrecomputedSctrTexDesc.Format = DXGI_FORMAT_R32G32B32A32_FLOAT; CComPtr ptex3DSctrRadiance, ptex3DInsctrOrder; CComPtr ptex3DSctrRadianceSRV, ptex3DInsctrOrderSRV; V_RETURN(pDevice->CreateTexture3D(&PrecomputedSctrTexDesc, NULL, &ptex3DSctrRadiance)); V_RETURN(pDevice->CreateTexture3D(&PrecomputedSctrTexDesc, NULL, &ptex3DInsctrOrder)); V_RETURN(pDevice->CreateShaderResourceView(ptex3DSctrRadiance, NULL, &ptex3DSctrRadianceSRV)); V_RETURN(pDevice->CreateShaderResourceView(ptex3DInsctrOrder, NULL, &ptex3DInsctrOrderSRV)); std::vector< CComPtr > ptex3DSctrRadianceRTVs(PrecomputedSctrTexDesc.Depth), ptex3DInsctrOrderRTVs(PrecomputedSctrTexDesc.Depth); for(UINT uiDepthSlice = 0; uiDepthSlice < PrecomputedSctrTexDesc.Depth; ++uiDepthSlice) { D3D11_RENDER_TARGET_VIEW_DESC CurrSliceRTVDesc; CurrSliceRTVDesc.Format = PrecomputedSctrTexDesc.Format; CurrSliceRTVDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE3D; CurrSliceRTVDesc.Texture3D.MipSlice = 0; CurrSliceRTVDesc.Texture3D.FirstWSlice = uiDepthSlice; CurrSliceRTVDesc.Texture3D.WSize = 1; V_RETURN(pDevice->CreateRenderTargetView(ptex3DSctrRadiance, &CurrSliceRTVDesc, &ptex3DSctrRadianceRTVs[uiDepthSlice])); V_RETURN(pDevice->CreateRenderTargetView(ptex3DInsctrOrder, &CurrSliceRTVDesc, &ptex3DInsctrOrderRTVs[uiDepthSlice])); } const int iNumScatteringOrders = 4; for(int iSctrOrder = 1; iSctrOrder < iNumScatteringOrders; ++iSctrOrder) { for(int iPass = 0; iPass < 3; ++iPass) { pContext->OMSetRenderTargets(0, NULL, NULL); // Pass 0: compute differential in-scattering // Pass 1: integrate differential in-scattering // Pass 2: accumulate total multiple scattering ID3D11ShaderResourceView *pSRVs[2] = {nullptr}; CRenderTechnique *pRenderTech = nullptr; std::vector< CComPtr > *pRTVs; switch(iPass) { case 0: // Pre-compute the radiance of light scattered at a given point in given direction. pRenderTech = &m_ComputeSctrRadianceTech; pRTVs = &ptex3DSctrRadianceRTVs; pSRVs[0] = (iSctrOrder == 1) ? m_ptex3DSingleScatteringSRV : ptex3DInsctrOrderSRV; pSRVs[1] = m_ptex2DSphereRandomSamplingSRV; break; case 1: // Compute in-scattering order for a given point and direction pRenderTech = &m_ComputeScatteringOrderTech; pRTVs = &ptex3DInsctrOrderRTVs; pSRVs[0] = ptex3DSctrRadianceSRV; break; case 2: // Accumulate in-scattering pRenderTech = &m_AddScatteringOrderTech; pRTVs = &ptex3DHighOrderSctrRTVs; pSRVs[0] = ptex3DInsctrOrderSRV; break; } for(UINT uiDepthSlice = 0; uiDepthSlice < PrecomputedSctrTexDesc.Depth; ++uiDepthSlice) { pContext->PSSetShaderResources(0, _countof(pSRVs), pSRVs); // Set sun zenith and sun view angles SMiscDynamicParams MiscDynamicParams = {NULL}; MiscDynamicParams.uiDepthSlice = uiDepthSlice; UINT uiW = uiDepthSlice % sm_iPrecomputedSctrWDim; UINT uiQ = uiDepthSlice / sm_iPrecomputedSctrWDim; MiscDynamicParams.f2WQ.x = ((float)uiW + 0.5f) / (float)sm_iPrecomputedSctrWDim; assert(0 < MiscDynamicParams.f2WQ.x && MiscDynamicParams.f2WQ.x < 1); MiscDynamicParams.f2WQ.y = ((float)uiQ + 0.5f) / (float)sm_iPrecomputedSctrQDim; assert(0 < MiscDynamicParams.f2WQ.y && MiscDynamicParams.f2WQ.y < 1); UpdateConstantBuffer(pContext, m_pcbMiscParams, &MiscDynamicParams, sizeof(MiscDynamicParams)); //cbuffer cbMiscDynamicParams : register( b4 ) pContext->PSSetConstantBuffers(4, 1, &m_pcbMiscParams.p); auto *pRTV = (ID3D11RenderTargetView*)(*pRTVs)[uiDepthSlice]; pContext->OMSetRenderTargets(1, &pRTV, NULL); RenderQuad( pContext, *pRenderTech, PrecomputedSctrTexDesc.Width, PrecomputedSctrTexDesc.Height ); } } } pContext->OMSetRenderTargets(0, NULL, NULL); // Combine single scattering and higher order scattering into single texture pContext->CopyResource(ptex3DMultipleSctr, ptex3DSingleSctr); for(UINT uiDepthSlice = 0; uiDepthSlice < PrecomputedSctrTexDesc.Depth; ++uiDepthSlice) { ID3D11ShaderResourceView *pSRVs[1] = {m_ptex3DHighOrderScatteringSRV}; pContext->PSSetShaderResources(0, _countof(pSRVs), pSRVs); SMiscDynamicParams MiscDynamicParams = {NULL}; MiscDynamicParams.uiDepthSlice = uiDepthSlice; UpdateConstantBuffer(pContext, m_pcbMiscParams, &MiscDynamicParams, sizeof(MiscDynamicParams)); //cbuffer cbMiscDynamicParams : register( b4 ) pContext->PSSetConstantBuffers(4, 1, &m_pcbMiscParams.p); auto *pRTV = (ID3D11RenderTargetView*)ptex3DMultipleSctrRTVs[uiDepthSlice]; pContext->OMSetRenderTargets(1, &pRTV, NULL); RenderQuad( pContext, m_AddScatteringOrderTech, PrecomputedSctrTexDesc.Width, PrecomputedSctrTexDesc.Height ); } pContext->OMSetRenderTargets(0, NULL, NULL); return S_OK; } void CLightSctrPostProcess :: ReconstructCameraSpaceZ(SFrameAttribs &FrameAttribs) { if( !m_ReconstrCamSpaceZTech.IsValid() ) { CD3DShaderMacroHelper Macros; DefineMacros(Macros); Macros.Finalize(); m_ReconstrCamSpaceZTech.SetDeviceAndContext(FrameAttribs.pd3dDevice, FrameAttribs.pd3dDeviceContext); m_ReconstrCamSpaceZTech.CreatePixelShaderFromFile( m_strEffectPath, "ReconstructCameraSpaceZPS", Macros ); m_ReconstrCamSpaceZTech.SetVS( m_pGenerateScreenSizeQuadVS ); m_ReconstrCamSpaceZTech.SetDS( m_pDisableDepthTestDS ); m_ReconstrCamSpaceZTech.SetRS( m_pSolidFillNoCullRS ); m_ReconstrCamSpaceZTech.SetBS( m_pDefaultBS ); } // Depth buffer is non-linear and cannot be interpolated directly // We have to reconstruct camera space z to be able to use bilinear filtering // Set render target first, because depth buffer is still bound on output and it must be unbound FrameAttribs.pd3dDeviceContext->OMSetRenderTargets(1, &m_ptex2DCameraSpaceZRTV.p, NULL); // Texture2D g_tex2DCamSpaceZ : register( t0 ); FrameAttribs.pd3dDeviceContext->PSSetShaderResources(0, 1, &FrameAttribs.ptex2DSrcDepthBufferSRV); RenderQuad( FrameAttribs.pd3dDeviceContext, m_ReconstrCamSpaceZTech, m_uiBackBufferWidth, m_uiBackBufferHeight ); FrameAttribs.pd3dDeviceContext->OMSetRenderTargets(0, NULL, NULL); } void CLightSctrPostProcess :: RenderSliceEndpoints(SFrameAttribs &FrameAttribs) { if( !m_RendedSliceEndpointsTech.IsValid() ) { m_RendedSliceEndpointsTech.SetDeviceAndContext(FrameAttribs.pd3dDevice, FrameAttribs.pd3dDeviceContext); CD3DShaderMacroHelper Macros; DefineMacros(Macros); Macros.Finalize(); m_RendedSliceEndpointsTech.CreatePixelShaderFromFile( m_strEffectPath, "GenerateSliceEndpointsPS", Macros ); m_RendedSliceEndpointsTech.SetVS( m_pGenerateScreenSizeQuadVS ); m_RendedSliceEndpointsTech.SetDS( m_pDisableDepthTestDS ); m_RendedSliceEndpointsTech.SetRS( m_pSolidFillNoCullRS ); m_RendedSliceEndpointsTech.SetBS( m_pDefaultBS ); } ID3D11RenderTargetView *ppRTVs[] = {m_ptex2DSliceEndpointsRTV}; FrameAttribs.pd3dDeviceContext->OMSetRenderTargets(_countof(ppRTVs), ppRTVs, NULL); RenderQuad( FrameAttribs.pd3dDeviceContext, m_RendedSliceEndpointsTech, m_PostProcessingAttribs.m_uiNumEpipolarSlices, 1 ); FrameAttribs.pd3dDeviceContext->OMSetRenderTargets(0, NULL, NULL); } void CLightSctrPostProcess :: RenderCoordinateTexture(SFrameAttribs &FrameAttribs) { if( !m_RendedCoordTexTech.IsValid() ) { m_RendedCoordTexTech.SetDeviceAndContext(FrameAttribs.pd3dDevice, FrameAttribs.pd3dDeviceContext); CD3DShaderMacroHelper Macros; DefineMacros(Macros); Macros.Finalize(); m_RendedCoordTexTech.CreatePixelShaderFromFile( m_strEffectPath, "GenerateCoordinateTexturePS", Macros ); m_RendedCoordTexTech.SetVS( m_pGenerateScreenSizeQuadVS ); m_RendedCoordTexTech.SetDS( m_pDisableDepthTestIncrStencilDS ); m_RendedCoordTexTech.SetRS( m_pSolidFillNoCullRS ); m_RendedCoordTexTech.SetBS( m_pDefaultBS ); } // Coordinate texture is a texture with dimensions [Total Samples X Num Slices] // Texel t[i,j] contains projection-space screen cooridantes of the i-th sample in j-th epipolar slice ID3D11RenderTargetView *ppRTVs[] = {m_ptex2DCoordinateTextureRTV, m_ptex2DEpipolarCamSpaceZRTV}; FrameAttribs.pd3dDeviceContext->OMSetRenderTargets(_countof(ppRTVs), ppRTVs, m_ptex2DEpipolarImageDSV); static const float fInvalidCoordinate = -1e+30f; // Both coord texture and epipolar CamSpaceZ are 32-bit float float InvalidCoords[] = {fInvalidCoordinate, fInvalidCoordinate, fInvalidCoordinate, fInvalidCoordinate}; // Clear both render targets with values that can't be correct projection space coordinates and camera space Z: FrameAttribs.pd3dDeviceContext->ClearRenderTargetView(m_ptex2DCoordinateTextureRTV, InvalidCoords); FrameAttribs.pd3dDeviceContext->ClearRenderTargetView(m_ptex2DEpipolarCamSpaceZRTV, InvalidCoords); // Clear depth stencil view. Since we use stencil part only, there is no need to clear depth // Set stencil value to 0 FrameAttribs.pd3dDeviceContext->ClearDepthStencilView(m_ptex2DEpipolarImageDSV, D3D11_CLEAR_STENCIL, 1.0f, 0); ID3D11ShaderResourceView *pSRVs[] = { m_ptex2DCameraSpaceZSRV, // Texture2D g_tex2DCamSpaceZ : register( t0 ); NULL, // Unused : register( t1 ) NULL, // Unused : register( t2 ) NULL, // Unused : register( t3 ) m_ptex2DSliceEndpointsSRV, // Texture2D g_tex2DSliceEndPoints : register( t4 ); }; FrameAttribs.pd3dDeviceContext->PSSetShaderResources(0, _countof(pSRVs), pSRVs); // Depth stencil state is configured to always increment stencil value. If coordinates are outside the screen, // the pixel shader discards the pixel and stencil value is left untouched. All such pixels will be skipped from // further processing RenderQuad( FrameAttribs.pd3dDeviceContext, m_RendedCoordTexTech, m_PostProcessingAttribs.m_uiMaxSamplesInSlice, m_PostProcessingAttribs.m_uiNumEpipolarSlices ); FrameAttribs.pd3dDeviceContext->OMSetRenderTargets(0, NULL, NULL); } void CLightSctrPostProcess :: RenderCoarseUnshadowedInctr(SFrameAttribs &FrameAttribs) { if( !m_RenderCoarseUnshadowedInsctrTech.IsValid() ) { m_RenderCoarseUnshadowedInsctrTech.SetDeviceAndContext(FrameAttribs.pd3dDevice, FrameAttribs.pd3dDeviceContext); CD3DShaderMacroHelper Macros; DefineMacros(Macros); Macros.Finalize(); m_RenderCoarseUnshadowedInsctrTech.CreatePixelShaderFromFile( m_strEffectPath, "RenderCoarseUnshadowedInsctrPS", Macros ); m_RenderCoarseUnshadowedInsctrTech.SetVS( m_pGenerateScreenSizeQuadVS ); m_RenderCoarseUnshadowedInsctrTech.SetDS( m_pNoDepth_StEqual_KeepStencilDS, 1); m_RenderCoarseUnshadowedInsctrTech.SetRS( m_pSolidFillNoCullRS ); m_RenderCoarseUnshadowedInsctrTech.SetBS( m_pDefaultBS ); } if( m_PostProcessingAttribs.m_uiExtinctionEvalMode == EXTINCTION_EVAL_MODE_EPIPOLAR && !m_ptex2DEpipolarExtinctionSRV ) { D3D11_TEXTURE2D_DESC EpipolarExtinctionTexDesc = { m_PostProcessingAttribs.m_uiMaxSamplesInSlice, //UINT Width; m_PostProcessingAttribs.m_uiNumEpipolarSlices, //UINT Height; 1, //UINT MipLevels; 1, //UINT ArraySize; DXGI_FORMAT_R8G8B8A8_UNORM, //DXGI_FORMAT Format; {1,0}, //DXGI_SAMPLE_DESC SampleDesc; D3D11_USAGE_DEFAULT, //D3D11_USAGE Usage; D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET, //UINT BindFlags; 0, //UINT CPUAccessFlags; 0, //UINT MiscFlags; }; CComPtr ptex2DEpipolarExtinction; // Create 2-D texture, shader resource and target view buffers on the device HRESULT hr; V( FrameAttribs.pd3dDevice->CreateTexture2D( &EpipolarExtinctionTexDesc, NULL, &ptex2DEpipolarExtinction) ); V( FrameAttribs.pd3dDevice->CreateShaderResourceView( ptex2DEpipolarExtinction, NULL, &m_ptex2DEpipolarExtinctionSRV) ); V( FrameAttribs.pd3dDevice->CreateRenderTargetView( ptex2DEpipolarExtinction, NULL, &m_ptex2DEpipolarExtinctionRTV) ); } ID3D11ShaderResourceView *pSRVs[] = { m_ptex2DCoordinateTextureSRV, //Texture2D g_tex2DCoordinates : register( t1 ); m_ptex2DEpipolarCamSpaceZSRV, //Texture2D g_tex2DEpipolarCamSpaceZ : register( t2 ); nullptr, // t3 nullptr, // t4 m_ptex2DOccludedNetDensityToAtmTopSRV, // Texture2D g_tex2DOccludedNetDensityToAtmTop : register( t5 ); nullptr, // t6 m_ptex3DSingleScatteringSRV, // Texture3D g_tex3DSingleSctrLUT : register( t7 ); m_ptex3DHighOrderScatteringSRV, // Texture3D g_tex3DHighOrderSctrLUT : register( t8 ); m_ptex3DMultipleScatteringSRV // Texture3D g_tex3DMultipleSctrLUT : register( t9 ); }; FrameAttribs.pd3dDeviceContext->PSSetShaderResources(1, _countof(pSRVs), pSRVs); float flt16max = 65504.f; // Epipolar Inscattering is 16-bit float const float InvalidInsctr[] = {-flt16max, -flt16max, -flt16max, -flt16max}; if( m_ptex2DEpipolarInscatteringRTV ) FrameAttribs.pd3dDeviceContext->ClearRenderTargetView(m_ptex2DEpipolarInscatteringRTV, InvalidInsctr); const float One[] = {1, 1, 1, 1}; if( m_ptex2DEpipolarExtinctionRTV ) FrameAttribs.pd3dDeviceContext->ClearRenderTargetView(m_ptex2DEpipolarExtinctionRTV, One); ID3D11RenderTargetView *pRTVs[] = {m_ptex2DEpipolarInscatteringRTV, m_ptex2DEpipolarExtinctionRTV}; FrameAttribs.pd3dDeviceContext->OMSetRenderTargets(_countof(pRTVs),pRTVs, m_ptex2DEpipolarImageDSV); RenderQuad( FrameAttribs.pd3dDeviceContext, m_RenderCoarseUnshadowedInsctrTech, m_PostProcessingAttribs.m_uiMaxSamplesInSlice, m_PostProcessingAttribs.m_uiNumEpipolarSlices ); FrameAttribs.pd3dDeviceContext->OMSetRenderTargets(0, NULL, NULL); } void CLightSctrPostProcess :: RefineSampleLocations(SFrameAttribs &FrameAttribs) { if( !m_RefineSampleLocationsTech.IsValid() ) { m_RefineSampleLocationsTech.SetDeviceAndContext(FrameAttribs.pd3dDevice, FrameAttribs.pd3dDeviceContext); // Thread group size must be at least as large as initial sample step m_uiSampleRefinementCSThreadGroupSize = max( m_uiSampleRefinementCSMinimumThreadGroupSize, m_PostProcessingAttribs.m_uiInitialSampleStepInSlice ); // Thread group size cannot be larger than the total number of samples in slice m_uiSampleRefinementCSThreadGroupSize = min( m_uiSampleRefinementCSThreadGroupSize, m_PostProcessingAttribs.m_uiMaxSamplesInSlice ); CD3DShaderMacroHelper Macros; DefineMacros(Macros); Macros.AddShaderMacro("INITIAL_SAMPLE_STEP", m_PostProcessingAttribs.m_uiInitialSampleStepInSlice); Macros.AddShaderMacro("THREAD_GROUP_SIZE" , m_uiSampleRefinementCSThreadGroupSize ); Macros.AddShaderMacro("REFINEMENT_CRITERION", m_PostProcessingAttribs.m_uiRefinementCriterion ); Macros.AddShaderMacro("AUTO_EXPOSURE", m_PostProcessingAttribs.m_bAutoExposure); Macros.Finalize(); m_RefineSampleLocationsTech.CreateComputeShaderFromFile( L"fx\\RefineSampleLocations.fx", "RefineSampleLocationsCS", Macros ); } ID3D11ShaderResourceView *pSRVs[] = { m_ptex2DCoordinateTextureSRV, //Texture2D g_tex2DCoordinates : register( t1 ); m_ptex2DEpipolarCamSpaceZSRV, //Texture2D g_tex2DEpipolarCamSpaceZ : register( t2 ); m_ptex2DEpipolarInscatteringSRV, // Texture2D g_tex2DScatteredColor : register( t3 ); nullptr, // t4 nullptr, // t5 nullptr, // t6 nullptr, // t7 nullptr, // t8 nullptr, // t9 m_ptex2DAverageLuminanceSRV // Texture2D g_tex2DAverageLuminance : register( t10 ); }; FrameAttribs.pd3dDeviceContext->CSSetShaderResources(1, _countof(pSRVs), pSRVs); FrameAttribs.pd3dDeviceContext->CSSetUnorderedAccessViews(0, 1, &m_ptex2DInterpolationSourcesUAV.p, NULL); // Using small group size is inefficient since a lot of SIMD lanes become idle m_RefineSampleLocationsTech.Apply(); FrameAttribs.pd3dDeviceContext->Dispatch( m_PostProcessingAttribs.m_uiMaxSamplesInSlice/m_uiSampleRefinementCSThreadGroupSize, m_PostProcessingAttribs.m_uiNumEpipolarSlices, 1); UnbindResources( FrameAttribs.pd3dDeviceContext ); } void CLightSctrPostProcess :: MarkRayMarchingSamples(SFrameAttribs &FrameAttribs) { if( !m_MarkRayMarchingSamplesInStencilTech.IsValid() ) { CD3DShaderMacroHelper Macros; DefineMacros(Macros); Macros.Finalize(); m_MarkRayMarchingSamplesInStencilTech.SetDeviceAndContext(FrameAttribs.pd3dDevice, FrameAttribs.pd3dDeviceContext); m_MarkRayMarchingSamplesInStencilTech.CreatePixelShaderFromFile( m_strEffectPath, "MarkRayMarchingSamplesInStencilPS", Macros ); m_MarkRayMarchingSamplesInStencilTech.SetVS( m_pGenerateScreenSizeQuadVS ); m_MarkRayMarchingSamplesInStencilTech.SetDS( m_pNoDepth_StEqual_IncrStencilDS, 1 ); m_MarkRayMarchingSamplesInStencilTech.SetRS( m_pSolidFillNoCullRS ); m_MarkRayMarchingSamplesInStencilTech.SetBS( m_pDefaultBS ); } // Mark ray marching samples in the stencil // The depth stencil state is configured to pass only pixels, whose stencil value equals 1. Thus all epipolar samples with // coordinates outsied the screen (generated on the previous pass) are automatically discarded. The pixel shader only // passes samples which are interpolated from themselves, the rest are discarded. Thus after this pass all ray // marching samples will be marked with 2 in stencil ID3D11RenderTargetView *pDummyRTV = NULL; FrameAttribs.pd3dDeviceContext->OMSetRenderTargets(1, &pDummyRTV, m_ptex2DEpipolarImageDSV); FrameAttribs.pd3dDeviceContext->PSSetShaderResources(6, 1, &m_ptex2DInterpolationSourcesSRV.p); // Texture2D g_tex2DInterpolationSource : register( t6 ); RenderQuad( FrameAttribs.pd3dDeviceContext, m_MarkRayMarchingSamplesInStencilTech, m_PostProcessingAttribs.m_uiMaxSamplesInSlice, m_PostProcessingAttribs.m_uiNumEpipolarSlices ); FrameAttribs.pd3dDeviceContext->OMSetRenderTargets(0, NULL, NULL); } void CLightSctrPostProcess :: RenderSliceUVDirAndOrig(SFrameAttribs &FrameAttribs) { if( !m_RenderSliceUVDirInSMTech.IsValid() ) { CD3DShaderMacroHelper Macros; DefineMacros(Macros); Macros.Finalize(); m_RenderSliceUVDirInSMTech.SetDeviceAndContext(FrameAttribs.pd3dDevice, FrameAttribs.pd3dDeviceContext); m_RenderSliceUVDirInSMTech.CreatePixelShaderFromFile( m_strEffectPath, "RenderSliceUVDirInShadowMapTexturePS", Macros ); m_RenderSliceUVDirInSMTech.SetVS( m_pGenerateScreenSizeQuadVS ); m_RenderSliceUVDirInSMTech.SetDS( m_pDisableDepthTestDS ); m_RenderSliceUVDirInSMTech.SetRS( m_pSolidFillNoCullRS ); m_RenderSliceUVDirInSMTech.SetBS( m_pDefaultBS ); } if( !m_ptex2DSliceUVDirAndOriginRTV || !m_ptex2DSliceUVDirAndOriginSRV ) { D3D11_TEXTURE2D_DESC SliceUVDirAndOriginDesc = { m_PostProcessingAttribs.m_uiNumEpipolarSlices, //UINT Width; m_PostProcessingAttribs.m_iNumCascades, //UINT Height; 1, //UINT MipLevels; 1, //UINT ArraySize; DXGI_FORMAT_R32G32B32A32_FLOAT, //DXGI_FORMAT Format; {1,0}, //DXGI_SAMPLE_DESC SampleDesc; D3D11_USAGE_DEFAULT, //D3D11_USAGE Usage; D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET, //UINT BindFlags; 0, //UINT CPUAccessFlags; 0, //UINT MiscFlags; }; CComPtr ptex2DSliceUVDirInShadowMap; // Create 2-D texture, shader resource and target view buffers on the device HRESULT hr; V( FrameAttribs.pd3dDevice->CreateTexture2D( &SliceUVDirAndOriginDesc, NULL, &ptex2DSliceUVDirInShadowMap) ); V( FrameAttribs.pd3dDevice->CreateShaderResourceView( ptex2DSliceUVDirInShadowMap, NULL, &m_ptex2DSliceUVDirAndOriginSRV) ); V( FrameAttribs.pd3dDevice->CreateRenderTargetView( ptex2DSliceUVDirInShadowMap, NULL, &m_ptex2DSliceUVDirAndOriginRTV) ); } // Render [Num Slices x 1] texture containing slice direction in shadow map UV space FrameAttribs.pd3dDeviceContext->OMSetRenderTargets( 1, &m_ptex2DSliceUVDirAndOriginRTV.p, NULL); ID3D11ShaderResourceView *pSRVs[] = { m_ptex2DCameraSpaceZSRV, // Texture2D g_tex2DCamSpaceZ : register( t0 ); NULL, // Unused : register( t1 ) NULL, // Unused : register( t2 ) NULL, // Unused : register( t3 ) m_ptex2DSliceEndpointsSRV, // Texture2D g_tex2DSliceEndPoints : register( t4 ); }; FrameAttribs.pd3dDeviceContext->PSSetShaderResources(0, _countof(pSRVs), pSRVs); RenderQuad( FrameAttribs.pd3dDeviceContext, m_RenderSliceUVDirInSMTech, m_PostProcessingAttribs.m_uiNumEpipolarSlices, m_PostProcessingAttribs.m_iNumCascades - m_PostProcessingAttribs.m_iFirstCascade, 0, m_PostProcessingAttribs.m_iFirstCascade); FrameAttribs.pd3dDeviceContext->OMSetRenderTargets(0, NULL, NULL); } void CLightSctrPostProcess :: Build1DMinMaxMipMap(SFrameAttribs &FrameAttribs, int iCascadeIndex) { if( !m_InitializeMinMaxShadowMapTech.IsValid() ) { CD3DShaderMacroHelper Macros; DefineMacros(Macros); Macros.AddShaderMacro("IS_32BIT_MIN_MAX_MAP", m_PostProcessingAttribs.m_bIs32BitMinMaxMipMap); Macros.Finalize(); m_InitializeMinMaxShadowMapTech.SetDeviceAndContext(FrameAttribs.pd3dDevice, FrameAttribs.pd3dDeviceContext); m_InitializeMinMaxShadowMapTech.CreatePixelShaderFromFile( m_strEffectPath, "InitializeMinMaxShadowMapPS", Macros ); m_InitializeMinMaxShadowMapTech.SetVS( m_pGenerateScreenSizeQuadVS ); m_InitializeMinMaxShadowMapTech.SetDS( m_pDisableDepthTestDS ); m_InitializeMinMaxShadowMapTech.SetRS( m_pSolidFillNoCullRS ); m_InitializeMinMaxShadowMapTech.SetBS( m_pDefaultBS ); } if( !m_ComputeMinMaxSMLevelTech.IsValid() ) { CD3DShaderMacroHelper Macros; DefineMacros(Macros); Macros.Finalize(); m_ComputeMinMaxSMLevelTech.SetDeviceAndContext(FrameAttribs.pd3dDevice, FrameAttribs.pd3dDeviceContext); m_ComputeMinMaxSMLevelTech.CreatePixelShaderFromFile( m_strEffectPath, "ComputeMinMaxShadowMapLevelPS", Macros ); m_ComputeMinMaxSMLevelTech.SetVS( m_pGenerateScreenSizeQuadVS ); m_ComputeMinMaxSMLevelTech.SetDS( m_pDisableDepthTestDS ); m_ComputeMinMaxSMLevelTech.SetRS( m_pSolidFillNoCullRS ); m_ComputeMinMaxSMLevelTech.SetBS( m_pDefaultBS ); } int iMinMaxTexHeight = m_PostProcessingAttribs.m_uiNumEpipolarSlices; if( m_bUseCombinedMinMaxTexture ) iMinMaxTexHeight *= (m_PostProcessingAttribs.m_iNumCascades - m_PostProcessingAttribs.m_iFirstCascade); // Computing min/max mip map using compute shader is much slower because a lot of threads are idle UINT uiXOffset = 0; UINT uiPrevXOffset = 0; UINT uiParity = 0; CComPtr presMinMaxShadowMap0, presMinMaxShadowMap1; m_ptex2DMinMaxShadowMapRTV[0]->GetResource(&presMinMaxShadowMap0); m_ptex2DMinMaxShadowMapRTV[1]->GetResource(&presMinMaxShadowMap1); #ifdef _DEBUG { D3D11_TEXTURE2D_DESC MinMaxShadowMapTexDesc; CComQIPtr(presMinMaxShadowMap0)->GetDesc(&MinMaxShadowMapTexDesc); assert( MinMaxShadowMapTexDesc.Width == m_PostProcessingAttribs.m_uiMinMaxShadowMapResolution ); assert( MinMaxShadowMapTexDesc.Height == iMinMaxTexHeight ); } #endif // Note that we start rendering min/max shadow map from step == 2 for(UINT iStep = 2; iStep <= (UINT)m_PostProcessingAttribs.m_fMaxShadowMapStep; iStep *=2, uiParity = (uiParity+1)%2 ) { // Use two buffers which are in turn used as the source and destination FrameAttribs.pd3dDeviceContext->OMSetRenderTargets( 1, &m_ptex2DMinMaxShadowMapRTV[uiParity].p, NULL); // Set source and destination min/max data offsets: SMiscDynamicParams MiscDynamicParams = {NULL}; MiscDynamicParams.ui4SrcMinMaxLevelXOffset = uiPrevXOffset; MiscDynamicParams.ui4DstMinMaxLevelXOffset = uiXOffset; MiscDynamicParams.fCascadeInd = static_cast(iCascadeIndex); UpdateConstantBuffer(FrameAttribs.pd3dDeviceContext, m_pcbMiscParams, &MiscDynamicParams, sizeof(MiscDynamicParams)); //cbuffer cbMiscDynamicParams : register( b4 ) FrameAttribs.pd3dDeviceContext->PSSetConstantBuffers(4, 1, &m_pcbMiscParams.p); if( iStep == 2 ) { // At the initial pass, the shader gathers 8 depths which will be used for // PCF filtering at the sample location and its next neighbor along the slice // and outputs min/max depths ID3D11ShaderResourceView *pSRVs[] = { FrameAttribs.ptex2DShadowMapSRV, // Texture2D g_tex2DLightSpaceDepthMap : register( t3 ); nullptr, // t4 nullptr, // t5 m_ptex2DSliceUVDirAndOriginSRV // Texture2D g_tex2DSliceUVDirAndOrigin : register( t6 ); }; FrameAttribs.pd3dDeviceContext->PSSetShaderResources( 3, _countof(pSRVs), pSRVs ); } else { // At the subsequent passes, the shader loads two min/max values from the next finer level // to compute next level of the binary tree // Texture2D g_tex2DMinMaxLightSpaceDepth : register( t4 ); FrameAttribs.pd3dDeviceContext->PSSetShaderResources( 4, 1, &m_ptex2DMinMaxShadowMapSRV[ (uiParity+1)%2 ].p ); } RenderQuad( FrameAttribs.pd3dDeviceContext, (iStep>2) ? m_ComputeMinMaxSMLevelTech : m_InitializeMinMaxShadowMapTech, m_PostProcessingAttribs.m_uiMinMaxShadowMapResolution / iStep, iMinMaxTexHeight, uiXOffset, 0 ); // All the data must reside in 0-th texture, so copy current level, if necessary, from 1-st texture if( uiParity == 1 ) { D3D11_BOX SrcBox; SrcBox.left = uiXOffset; SrcBox.right = uiXOffset + m_PostProcessingAttribs.m_uiMinMaxShadowMapResolution / iStep; SrcBox.top = 0; SrcBox.bottom = iMinMaxTexHeight; SrcBox.front = 0; SrcBox.back = 1; FrameAttribs.pd3dDeviceContext->CopySubresourceRegion(presMinMaxShadowMap0, 0, uiXOffset, 0, 0, presMinMaxShadowMap1, 0, &SrcBox); } uiPrevXOffset = uiXOffset; uiXOffset += m_PostProcessingAttribs.m_uiMinMaxShadowMapResolution / iStep; } FrameAttribs.pd3dDeviceContext->OMSetRenderTargets(0, NULL, NULL); } void CLightSctrPostProcess :: DoRayMarching(SFrameAttribs &FrameAttribs, UINT uiMaxStepsAlongRay, const SShadowMapAttribs &SMAttribs, int iCascadeIndex) { CRenderTechnique &DoRayMarchTech = m_DoRayMarchTech[m_PostProcessingAttribs.m_bUse1DMinMaxTree ? 1 : 0]; if( !DoRayMarchTech.IsValid() ) { CD3DShaderMacroHelper Macros; DefineMacros(Macros); Macros.AddShaderMacro("CASCADE_PROCESSING_MODE", m_PostProcessingAttribs.m_uiCascadeProcessingMode); Macros.Finalize(); DoRayMarchTech.SetDeviceAndContext(FrameAttribs.pd3dDevice, FrameAttribs.pd3dDeviceContext); DoRayMarchTech.CreatePixelShaderFromFile( m_strEffectPath, m_PostProcessingAttribs.m_bUse1DMinMaxTree ? "RayMarchMinMaxOptPS" : "RayMarchPS", Macros ); DoRayMarchTech.SetVS( m_pGenerateScreenSizeQuadVS ); // Sample locations for which ray marching should be performed will be marked in stencil with 2 DoRayMarchTech.SetDS( m_pNoDepth_StEqual_KeepStencilDS, 2 ); DoRayMarchTech.SetRS( m_pSolidFillNoCullRS ); DoRayMarchTech.SetBS( m_pAdditiveBlendBS ); } //float BlackColor[] = {0,0,0,0}; // NOTE: this is for debug purposes only: //FrameAttribs.pd3dDeviceContext->ClearRenderTargetView(m_ptex2DInitialScatteredLightRTV, BlackColor); ID3D11RenderTargetView *pRTVs[] = {m_ptex2DInitialScatteredLightRTV}; FrameAttribs.pd3dDeviceContext->OMSetRenderTargets(_countof(pRTVs), pRTVs, m_ptex2DEpipolarImageDSV); SMiscDynamicParams MiscDynamicParams = {NULL}; MiscDynamicParams.fMaxStepsAlongRay = static_cast( uiMaxStepsAlongRay ); MiscDynamicParams.fCascadeInd = static_cast(iCascadeIndex); UpdateConstantBuffer(FrameAttribs.pd3dDeviceContext, m_pcbMiscParams, &MiscDynamicParams, sizeof(MiscDynamicParams)); //cbuffer cbMiscDynamicParams : register( b4 ) FrameAttribs.pd3dDeviceContext->PSSetConstantBuffers(4, 1, &m_pcbMiscParams.p); ID3D11ShaderResourceView *pSRVs[] = { m_ptex2DCameraSpaceZSRV, // Texture2D g_tex2DCamSpaceZ : register( t0 ); m_ptex2DCoordinateTextureSRV, // Texture2D g_tex2DCoordinates : register( t1 ); m_ptex2DEpipolarCamSpaceZSRV, //Texture2D g_tex2DEpipolarCamSpaceZ : register( t2 ); FrameAttribs.ptex2DShadowMapSRV, // Texture2D g_tex2DLightSpaceDepthMap : register( t3 ); m_ptex2DMinMaxShadowMapSRV[0], // Texture2D g_tex2DMinMaxLightSpaceDepth : register( t4 ); m_ptex2DOccludedNetDensityToAtmTopSRV, // Texture2D g_tex2DOccludedNetDensityToAtmTop : register( t5 ); m_ptex2DSliceUVDirAndOriginSRV, // Texture2D g_tex2DSliceUVDirAndOrigin : register( t6 ); m_ptex3DSingleScatteringSRV, // Texture3D g_tex3DSingleSctrLUT : register( t7 ); m_ptex3DHighOrderScatteringSRV, // Texture3D g_tex3DHighOrderSctrLUT : register( t8 ); m_ptex3DMultipleScatteringSRV // Texture3D g_tex3DMultipleSctrLUT : register( t9 ); }; FrameAttribs.pd3dDeviceContext->PSSetShaderResources(0, _countof(pSRVs), pSRVs); int iNumInst = 0; if( m_PostProcessingAttribs.m_bEnableLightShafts ) { switch(m_PostProcessingAttribs.m_uiCascadeProcessingMode) { case CASCADE_PROCESSING_MODE_SINGLE_PASS: case CASCADE_PROCESSING_MODE_MULTI_PASS: iNumInst = 1; break; case CASCADE_PROCESSING_MODE_MULTI_PASS_INST: iNumInst = m_PostProcessingAttribs.m_iNumCascades - m_PostProcessingAttribs.m_iFirstCascade; break; } } else { iNumInst = 1; } // Depth stencil view now contains 2 for these pixels, for which ray marchings is to be performed // Depth stencil state is configured to pass only these pixels and discard the rest RenderQuad( FrameAttribs.pd3dDeviceContext, DoRayMarchTech, m_PostProcessingAttribs.m_uiMaxSamplesInSlice, m_PostProcessingAttribs.m_uiNumEpipolarSlices, 0,0, iNumInst); FrameAttribs.pd3dDeviceContext->OMSetRenderTargets(0, NULL, NULL); } void CLightSctrPostProcess :: InterpolateInsctrIrradiance(SFrameAttribs &FrameAttribs) { if( !m_InterpolateIrradianceTech.IsValid() ) { CD3DShaderMacroHelper Macros; DefineMacros(Macros); Macros.Finalize(); m_InterpolateIrradianceTech.SetDeviceAndContext(FrameAttribs.pd3dDevice, FrameAttribs.pd3dDeviceContext); m_InterpolateIrradianceTech.CreatePixelShaderFromFile( m_strEffectPath, "InterpolateIrradiancePS", Macros ); m_InterpolateIrradianceTech.SetVS( m_pGenerateScreenSizeQuadVS ); m_InterpolateIrradianceTech.SetDS( m_pDisableDepthTestDS ); m_InterpolateIrradianceTech.SetRS( m_pSolidFillNoCullRS ); m_InterpolateIrradianceTech.SetBS( m_pDefaultBS ); } ID3D11RenderTargetView *pRTVs[] = {m_ptex2DEpipolarInscatteringRTV}; FrameAttribs.pd3dDeviceContext->OMSetRenderTargets(_countof(pRTVs), pRTVs, m_ptex2DEpipolarImageDSV); ID3D11ShaderResourceView *pSRVs[] = { m_ptex2DInitialScatteredLightSRV, // Texture2D g_tex2DInitialInsctrIrradiance: register( t5 ); m_ptex2DInterpolationSourcesSRV // Texture2D g_tex2DInterpolationSource : register( t6 ); }; FrameAttribs.pd3dDeviceContext->PSSetShaderResources(5, _countof(pSRVs), pSRVs); RenderQuad( FrameAttribs.pd3dDeviceContext, m_InterpolateIrradianceTech, m_PostProcessingAttribs.m_uiMaxSamplesInSlice, m_PostProcessingAttribs.m_uiNumEpipolarSlices ); FrameAttribs.pd3dDeviceContext->OMSetRenderTargets(0, NULL, NULL); } void CLightSctrPostProcess :: CreateLowResLuminanceTexture(ID3D11Device *pDevice) { D3D11_TEXTURE2D_DESC LowResLuminanceTexDesc = { 1 << (sm_iLowResLuminanceMips-1), //UINT Width; 1 << (sm_iLowResLuminanceMips-1), //UINT Height; sm_iLowResLuminanceMips, //UINT MipLevels; 1, //UINT ArraySize; DXGI_FORMAT_R16_FLOAT, //DXGI_FORMAT Format; {1,0}, //DXGI_SAMPLE_DESC SampleDesc; D3D11_USAGE_DEFAULT, //D3D11_USAGE Usage; D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE, //UINT BindFlags; 0, //UINT CPUAccessFlags; D3D11_RESOURCE_MISC_GENERATE_MIPS //UINT MiscFlags; }; CComPtr ptex2DLowResLuminance; // Create 2-D texture, shader resource and target view buffers on the device HRESULT hr; V( pDevice->CreateTexture2D( &LowResLuminanceTexDesc, NULL, &ptex2DLowResLuminance) ); V( pDevice->CreateShaderResourceView( ptex2DLowResLuminance, NULL, &m_ptex2DLowResLuminanceSRV) ); V( pDevice->CreateRenderTargetView( ptex2DLowResLuminance, NULL, &m_ptex2DLowResLuminanceRTV) ); // Create 2-D texture, shader resource and target view buffers on the device LowResLuminanceTexDesc.Width = 1; LowResLuminanceTexDesc.Height = 1; LowResLuminanceTexDesc.MipLevels = 1; LowResLuminanceTexDesc.MiscFlags = 0; int Zero = 0; D3D11_SUBRESOURCE_DATA InitData = {&Zero, 2, 0}; CComPtr ptex2DAverageLuminance; V( pDevice->CreateTexture2D( &LowResLuminanceTexDesc, &InitData, &ptex2DAverageLuminance) ); V( pDevice->CreateShaderResourceView( ptex2DAverageLuminance, NULL, &m_ptex2DAverageLuminanceSRV) ); V( pDevice->CreateRenderTargetView( ptex2DAverageLuminance, NULL, &m_ptex2DAverageLuminanceRTV) ); } void CLightSctrPostProcess :: UnwarpEpipolarScattering(SFrameAttribs &FrameAttribs, bool bRenderLuminance) { if( !m_UnwarpEpipolarSctrImgTech.IsValid() ) { CD3DShaderMacroHelper Macros; DefineMacros(Macros); Macros.AddShaderMacro("PERFORM_TONE_MAPPING", true); Macros.AddShaderMacro("AUTO_EXPOSURE", m_PostProcessingAttribs.m_bAutoExposure); Macros.AddShaderMacro("TONE_MAPPING_MODE", m_PostProcessingAttribs.m_uiToneMappingMode); Macros.AddShaderMacro("CORRECT_INSCATTERING_AT_DEPTH_BREAKS", m_PostProcessingAttribs.m_bCorrectScatteringAtDepthBreaks); Macros.Finalize(); m_UnwarpEpipolarSctrImgTech.SetDeviceAndContext(FrameAttribs.pd3dDevice, FrameAttribs.pd3dDeviceContext); m_UnwarpEpipolarSctrImgTech.CreatePixelShaderFromFile( m_strEffectPath, "ApplyInscatteredRadiancePS", Macros ); m_UnwarpEpipolarSctrImgTech.SetVS( m_pGenerateScreenSizeQuadVS ); m_UnwarpEpipolarSctrImgTech.SetDS( m_pDisableDepthTestIncrStencilDS ); m_UnwarpEpipolarSctrImgTech.SetRS( m_pSolidFillNoCullRS ); m_UnwarpEpipolarSctrImgTech.SetBS( m_pDefaultBS ); } if( !m_UnwarpAndRenderLuminanceTech.IsValid() ) { CD3DShaderMacroHelper Macros; DefineMacros(Macros); Macros.AddShaderMacro("PERFORM_TONE_MAPPING", false); Macros.AddShaderMacro("CORRECT_INSCATTERING_AT_DEPTH_BREAKS", false); Macros.Finalize(); m_UnwarpAndRenderLuminanceTech.SetDeviceAndContext(FrameAttribs.pd3dDevice, FrameAttribs.pd3dDeviceContext); m_UnwarpAndRenderLuminanceTech.CreatePixelShaderFromFile( m_strEffectPath, "ApplyInscatteredRadiancePS", Macros ); m_UnwarpAndRenderLuminanceTech.SetVS( m_pGenerateScreenSizeQuadVS ); // Do not use stencil m_UnwarpAndRenderLuminanceTech.SetDS( m_pDisableDepthTestDS ); m_UnwarpAndRenderLuminanceTech.SetRS( m_pSolidFillNoCullRS ); m_UnwarpAndRenderLuminanceTech.SetBS( m_pDefaultBS ); } CRenderTechnique &Tech = bRenderLuminance ? m_UnwarpAndRenderLuminanceTech : m_UnwarpEpipolarSctrImgTech; ID3D11ShaderResourceView *pSRVs[] = { m_ptex2DCameraSpaceZSRV, // Texture2D g_tex2DCamSpaceZ : register( t0 ); FrameAttribs.ptex2DSrcColorBufferSRV, // Texture2D g_tex2DColorBuffer : register( t1 ); m_ptex2DEpipolarCamSpaceZSRV, // Texture2D g_tex2DEpipolarCamSpaceZ : register( t2 ); m_ptex2DEpipolarInscatteringSRV, // Texture2D g_tex2DScatteredColor : register( t3 ); m_ptex2DSliceEndpointsSRV, // Texture2D g_tex2DSliceEndPoints : register( t4 ); m_ptex2DOccludedNetDensityToAtmTopSRV, // Texture2D g_tex2DOccludedNetDensityToAtmTop : register( t5 ); m_ptex2DEpipolarExtinctionSRV, // Texture2D g_tex2DEpipolarExtinction : register( t6 ); nullptr, // t7 nullptr, // t8 nullptr, // t9 m_ptex2DAverageLuminanceSRV // Texture2D g_tex2DAverageLuminance : register( t10 ); }; FrameAttribs.pd3dDeviceContext->PSSetShaderResources(0, _countof(pSRVs), pSRVs); // Unwarp inscattering image and apply it to attenuated backgorund RenderQuad( FrameAttribs.pd3dDeviceContext, Tech ); } void CLightSctrPostProcess :: UpdateAverageLuminance(SFrameAttribs &FrameAttribs) { if( !m_UpdateAverageLuminanceTech.IsValid() ) { CD3DShaderMacroHelper Macros; DefineMacros(Macros); Macros.AddShaderMacro( "LIGHT_ADAPTATION", m_PostProcessingAttribs.m_bLightAdaptation ); Macros.AddShaderMacro("LOW_RES_LUMINANCE_MIPS", sm_iLowResLuminanceMips); Macros.Finalize(); m_UpdateAverageLuminanceTech.SetDeviceAndContext(FrameAttribs.pd3dDevice, FrameAttribs.pd3dDeviceContext); m_UpdateAverageLuminanceTech.CreatePixelShaderFromFile( m_strEffectPath, "UpdateAverageLuminancePS", Macros ); m_UpdateAverageLuminanceTech.SetVS( m_pGenerateScreenSizeQuadVS ); m_UpdateAverageLuminanceTech.SetDS( m_pDisableDepthTestDS ); m_UpdateAverageLuminanceTech.SetRS( m_pSolidFillNoCullRS ); m_UpdateAverageLuminanceTech.SetBS( m_pAlphaBlendBS ); } ID3D11RenderTargetView *pRTVs[] = {m_ptex2DAverageLuminanceRTV}; FrameAttribs.pd3dDeviceContext->OMSetRenderTargets(_countof(pRTVs), pRTVs, nullptr); ID3D11ShaderResourceView *pSRVs[] = {m_ptex2DLowResLuminanceSRV}; FrameAttribs.pd3dDeviceContext->PSSetShaderResources(0, _countof(pSRVs), pSRVs); SMiscDynamicParams MiscDynamicParams = {NULL}; MiscDynamicParams.fElapsedTime = (float)FrameAttribs.dElapsedTime; UpdateConstantBuffer(FrameAttribs.pd3dDeviceContext, m_pcbMiscParams, &MiscDynamicParams, sizeof(MiscDynamicParams)); //cbuffer cbMiscDynamicParams : register( b4 ) FrameAttribs.pd3dDeviceContext->PSSetConstantBuffers(4, 1, &m_pcbMiscParams.p); // Update average luminance RenderQuad( FrameAttribs.pd3dDeviceContext, m_UpdateAverageLuminanceTech, 1, 1 ); } void CLightSctrPostProcess :: FixInscatteringAtDepthBreaks(SFrameAttribs &FrameAttribs, UINT uiMaxStepsAlongRay, const SShadowMapAttribs &SMAttribs, bool bRenderLuminance) { bool bApplBG = true; auto &FixInsctrAtDepthBreaksTech = m_FixInsctrAtDepthBreaksTech[(bApplBG ? 1 : 0) + (bRenderLuminance ? 2 : 0)]; if( !FixInsctrAtDepthBreaksTech.IsValid() ) { CD3DShaderMacroHelper Macros; DefineMacros(Macros); Macros.AddShaderMacro("CASCADE_PROCESSING_MODE", CASCADE_PROCESSING_MODE_SINGLE_PASS); Macros.AddShaderMacro("PERFORM_TONE_MAPPING", !bRenderLuminance); Macros.AddShaderMacro("AUTO_EXPOSURE", m_PostProcessingAttribs.m_bAutoExposure); Macros.AddShaderMacro("TONE_MAPPING_MODE", m_PostProcessingAttribs.m_uiToneMappingMode); Macros.Finalize(); FixInsctrAtDepthBreaksTech.SetDeviceAndContext(FrameAttribs.pd3dDevice, FrameAttribs.pd3dDeviceContext); FixInsctrAtDepthBreaksTech.CreatePixelShaderFromFile( m_strEffectPath, bApplBG ? "FixAndApplyInscatteredRadiancePS" : "FixInscatteredRadiancePS", Macros ); FixInsctrAtDepthBreaksTech.SetVS( m_pGenerateScreenSizeQuadVS ); // If rendering luminance only, disable depth and stencil tests to render all pixels FixInsctrAtDepthBreaksTech.SetDS( bRenderLuminance ? m_pDisableDepthTestDS : m_pNoDepth_StEqual_KeepStencilDS, 0 ); FixInsctrAtDepthBreaksTech.SetRS( m_pSolidFillNoCullRS ); // If rendering luminance only, use default blend state to overwrite old luminance values FixInsctrAtDepthBreaksTech.SetBS( bRenderLuminance ? m_pDefaultBS : m_pAdditiveBlendBS ); } ID3D11ShaderResourceView *pSRVs[] = { m_ptex2DCameraSpaceZSRV, // Texture2D g_tex2DCamSpaceZ : register( t0 ); FrameAttribs.ptex2DSrcColorBufferSRV, // Texture2D g_tex2DColorBuffer : register( t1 ); m_ptex2DEpipolarCamSpaceZSRV, // Texture2D g_tex2DEpipolarCamSpaceZ : register( t2 ); FrameAttribs.ptex2DShadowMapSRV, // Texture2D g_tex2DLightSpaceDepthMap : register( t3 ); m_ptex2DMinMaxShadowMapSRV[0], // Texture2D g_tex2DMinMaxLightSpaceDepth : register( t4 ); m_ptex2DOccludedNetDensityToAtmTopSRV, // Texture2D g_tex2DOccludedNetDensityToAtmTop : register( t5 ); m_ptex2DSliceUVDirAndOriginSRV, // Texture2D g_tex2DSliceUVDirAndOrigin : register( t6 ); m_ptex3DSingleScatteringSRV, // Texture3D g_tex3DSingleSctrLUT : register( t7 ); m_ptex3DHighOrderScatteringSRV, // Texture3D g_tex3DHighOrderSctrLUT : register( t8 ); m_ptex3DMultipleScatteringSRV, // Texture3D g_tex3DMultipleSctrLUT : register( t9 ); m_ptex2DAverageLuminanceSRV // Texture2D g_tex2DAverageLuminance : register( t10 ); }; FrameAttribs.pd3dDeviceContext->PSSetShaderResources(0, _countof(pSRVs), pSRVs); SMiscDynamicParams MiscDynamicParams = {NULL}; MiscDynamicParams.fMaxStepsAlongRay = static_cast( uiMaxStepsAlongRay ); MiscDynamicParams.fCascadeInd = static_cast(m_PostProcessingAttribs.m_iFirstCascade); UpdateConstantBuffer(FrameAttribs.pd3dDeviceContext, m_pcbMiscParams, &MiscDynamicParams, sizeof(MiscDynamicParams)); //cbuffer cbMiscDynamicParams : register( b4 ) FrameAttribs.pd3dDeviceContext->PSSetConstantBuffers(4, 1, &m_pcbMiscParams.p); RenderQuad( FrameAttribs.pd3dDeviceContext, FixInsctrAtDepthBreaksTech); } void CLightSctrPostProcess :: RenderSampleLocations(SFrameAttribs &FrameAttribs) { if( !m_RenderSampleLocationsTech.IsValid() ) { CD3DShaderMacroHelper Macros; DefineMacros(Macros); Macros.Finalize(); m_RenderSampleLocationsTech.SetDeviceAndContext(FrameAttribs.pd3dDevice, FrameAttribs.pd3dDeviceContext); m_RenderSampleLocationsTech.CreateVGPShadersFromFile( m_strEffectPath, "PassThroughVS", "RenderSamplePositionsGS", "RenderSampleLocationsPS", Macros ); m_RenderSampleLocationsTech.SetDS( m_pDisableDepthTestDS ); m_RenderSampleLocationsTech.SetRS( m_pSolidFillNoCullRS ); D3D11_BLEND_DESC OverBlendStateDesc; ZeroMemory(&OverBlendStateDesc, sizeof(OverBlendStateDesc)); OverBlendStateDesc.IndependentBlendEnable = FALSE; OverBlendStateDesc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; OverBlendStateDesc.RenderTarget[0].BlendEnable = TRUE; OverBlendStateDesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD; OverBlendStateDesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD; OverBlendStateDesc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA; OverBlendStateDesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO; OverBlendStateDesc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA; OverBlendStateDesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE; CComPtr pOverBS; HRESULT hr; V( FrameAttribs.pd3dDevice->CreateBlendState( &OverBlendStateDesc, &pOverBS) ); m_RenderSampleLocationsTech.SetBS( pOverBS ); } ID3D11ShaderResourceView *pSRVs[] = { NULL, m_ptex2DCoordinateTextureSRV, // Texture2D g_tex2DCoordinates : register( t1 ); NULL, // t2 NULL, // t3 NULL, // t4 NULL, // t5 m_ptex2DInterpolationSourcesSRV, // Texture2D g_tex2DInterpolationSource : register( t6 ); }; FrameAttribs.pd3dDeviceContext->GSSetShaderResources(0, _countof(pSRVs), pSRVs); FrameAttribs.pd3dDeviceContext->OMSetRenderTargets(1, &FrameAttribs.pDstRTV, NULL); FrameAttribs.pd3dDeviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_POINTLIST); UINT offset[1] = {0}; UINT stride[1] = {0}; ID3D11Buffer *ppBuffers[1] = {0}; // Set the device's first and only vertex buffer with zero stride and offset FrameAttribs.pd3dDeviceContext->IASetVertexBuffers(0,1,ppBuffers,stride,offset); // There is no input-layout object and the primitive topology is triangle strip FrameAttribs.pd3dDeviceContext->IASetInputLayout(NULL); m_RenderSampleLocationsTech.Apply(); FrameAttribs.pd3dDeviceContext->Draw(m_PostProcessingAttribs.m_uiMaxSamplesInSlice * m_PostProcessingAttribs.m_uiNumEpipolarSlices,0); UnbindResources( FrameAttribs.pd3dDeviceContext ); } void CLightSctrPostProcess :: PerformPostProcessing(SFrameAttribs &FrameAttribs, SPostProcessingAttribs &PPAttribs) { HRESULT hr; if( GetAsyncKeyState(VK_F8) ) { m_ReconstrCamSpaceZTech.Release(); m_RendedSliceEndpointsTech.Release(); m_RendedCoordTexTech.Release(); m_RefineSampleLocationsTech.Release(); m_RenderCoarseUnshadowedInsctrTech.Release(); m_MarkRayMarchingSamplesInStencilTech.Release(); m_RenderSliceUVDirInSMTech.Release(); m_InitializeMinMaxShadowMapTech.Release(); m_ComputeMinMaxSMLevelTech.Release(); for(size_t i=0; i<_countof(m_DoRayMarchTech); ++i) m_DoRayMarchTech[i].Release(); m_InterpolateIrradianceTech.Release(); m_UnwarpEpipolarSctrImgTech.Release(); m_UnwarpAndRenderLuminanceTech.Release(); m_UpdateAverageLuminanceTech.Release(); for(size_t i=0; i<_countof(m_FixInsctrAtDepthBreaksTech); ++i) m_FixInsctrAtDepthBreaksTech[i].Release(); m_RenderSampleLocationsTech.Release(); m_RenderSunTech.Release(); m_PrecomputeSingleSctrTech.Release(); m_ComputeSctrRadianceTech.Release(); m_ComputeScatteringOrderTech.Release(); m_AddScatteringOrderTech.Release(); } bool bUseCombinedMinMaxTexture = PPAttribs.m_uiCascadeProcessingMode == CASCADE_PROCESSING_MODE_SINGLE_PASS || PPAttribs.m_uiCascadeProcessingMode == CASCADE_PROCESSING_MODE_MULTI_PASS_INST || PPAttribs.m_bCorrectScatteringAtDepthBreaks || PPAttribs.m_uiLightSctrTechnique == LIGHT_SCTR_TECHNIQUE_BRUTE_FORCE; if( PPAttribs.m_uiNumEpipolarSlices != m_PostProcessingAttribs.m_uiNumEpipolarSlices || PPAttribs.m_uiMaxSamplesInSlice != m_PostProcessingAttribs.m_uiMaxSamplesInSlice || PPAttribs.m_bOptimizeSampleLocations != m_PostProcessingAttribs.m_bOptimizeSampleLocations ) m_RendedSliceEndpointsTech.Release(); if( PPAttribs.m_uiMaxSamplesInSlice != m_PostProcessingAttribs.m_uiMaxSamplesInSlice ) m_RendedCoordTexTech.Release(); if( PPAttribs.m_uiMaxSamplesInSlice != m_PostProcessingAttribs.m_uiMaxSamplesInSlice || PPAttribs.m_uiInitialSampleStepInSlice != m_PostProcessingAttribs.m_uiInitialSampleStepInSlice || PPAttribs.m_uiRefinementCriterion != m_PostProcessingAttribs.m_uiRefinementCriterion || PPAttribs.m_bAutoExposure != m_PostProcessingAttribs.m_bAutoExposure ) m_RefineSampleLocationsTech.Release(); if( PPAttribs.m_bUse1DMinMaxTree != m_PostProcessingAttribs.m_bUse1DMinMaxTree || bUseCombinedMinMaxTexture != m_bUseCombinedMinMaxTexture || PPAttribs.m_uiNumEpipolarSlices != m_PostProcessingAttribs.m_uiNumEpipolarSlices || PPAttribs.m_bIs32BitMinMaxMipMap != m_PostProcessingAttribs.m_bIs32BitMinMaxMipMap ) { m_InitializeMinMaxShadowMapTech.Release(); } if( PPAttribs.m_bUse1DMinMaxTree != m_PostProcessingAttribs.m_bUse1DMinMaxTree || PPAttribs.m_uiCascadeProcessingMode != m_PostProcessingAttribs.m_uiCascadeProcessingMode || bUseCombinedMinMaxTexture != m_bUseCombinedMinMaxTexture || PPAttribs.m_bEnableLightShafts != m_PostProcessingAttribs.m_bEnableLightShafts || PPAttribs.m_uiMultipleScatteringMode != m_PostProcessingAttribs.m_uiMultipleScatteringMode || PPAttribs.m_uiSingleScatteringMode != m_PostProcessingAttribs.m_uiSingleScatteringMode) { for(int i=0; i<_countof(m_DoRayMarchTech); ++i) m_DoRayMarchTech[i].Release(); } if( PPAttribs.m_uiNumEpipolarSlices != m_PostProcessingAttribs.m_uiNumEpipolarSlices || PPAttribs.m_uiMaxSamplesInSlice != m_PostProcessingAttribs.m_uiMaxSamplesInSlice ) { m_UnwarpEpipolarSctrImgTech.Release(); m_UnwarpAndRenderLuminanceTech.Release(); } if( PPAttribs.m_bAutoExposure != m_PostProcessingAttribs.m_bAutoExposure || PPAttribs.m_uiToneMappingMode != m_PostProcessingAttribs.m_uiToneMappingMode || PPAttribs.m_bCorrectScatteringAtDepthBreaks != m_PostProcessingAttribs.m_bCorrectScatteringAtDepthBreaks ) { m_UnwarpEpipolarSctrImgTech.Release(); } if( PPAttribs.m_bLightAdaptation != m_PostProcessingAttribs.m_bLightAdaptation ) { m_UpdateAverageLuminanceTech.Release(); } if( PPAttribs.m_uiCascadeProcessingMode != m_PostProcessingAttribs.m_uiCascadeProcessingMode || bUseCombinedMinMaxTexture != m_bUseCombinedMinMaxTexture || PPAttribs.m_bEnableLightShafts != m_PostProcessingAttribs.m_bEnableLightShafts || PPAttribs.m_uiMultipleScatteringMode != m_PostProcessingAttribs.m_uiMultipleScatteringMode || PPAttribs.m_uiSingleScatteringMode != m_PostProcessingAttribs.m_uiSingleScatteringMode || PPAttribs.m_bAutoExposure != m_PostProcessingAttribs.m_bAutoExposure || PPAttribs.m_uiToneMappingMode != m_PostProcessingAttribs.m_uiToneMappingMode) { for(size_t i=0; i<_countof(m_FixInsctrAtDepthBreaksTech); ++i) m_FixInsctrAtDepthBreaksTech[i].Release(); } if( PPAttribs.m_uiMaxSamplesInSlice != m_PostProcessingAttribs.m_uiMaxSamplesInSlice || PPAttribs.m_uiNumEpipolarSlices != m_PostProcessingAttribs.m_uiNumEpipolarSlices ) { m_ptex2DCoordinateTextureRTV.Release(); m_ptex2DCoordinateTextureSRV.Release(); } if( PPAttribs.m_uiMinMaxShadowMapResolution != m_PostProcessingAttribs.m_uiMinMaxShadowMapResolution || PPAttribs.m_uiNumEpipolarSlices != m_PostProcessingAttribs.m_uiNumEpipolarSlices || PPAttribs.m_bUse1DMinMaxTree != m_PostProcessingAttribs.m_bUse1DMinMaxTree || PPAttribs.m_bIs32BitMinMaxMipMap != m_PostProcessingAttribs.m_bIs32BitMinMaxMipMap || bUseCombinedMinMaxTexture != m_bUseCombinedMinMaxTexture || bUseCombinedMinMaxTexture && (PPAttribs.m_iFirstCascade != m_PostProcessingAttribs.m_iFirstCascade || PPAttribs.m_iNumCascades != m_PostProcessingAttribs.m_iNumCascades) ) { for(int i=0; i < _countof(m_ptex2DMinMaxShadowMapSRV); ++i) m_ptex2DMinMaxShadowMapSRV[i].Release(); for(int i=0; i < _countof(m_ptex2DMinMaxShadowMapRTV); ++i) m_ptex2DMinMaxShadowMapRTV[i].Release(); } if( PPAttribs.m_iNumCascades != m_PostProcessingAttribs.m_iNumCascades ) { m_ptex2DSliceUVDirAndOriginSRV.Release(); m_ptex2DSliceUVDirAndOriginRTV.Release(); } if( PPAttribs.m_uiCascadeProcessingMode != m_PostProcessingAttribs.m_uiCascadeProcessingMode ) { m_ComputeMinMaxSMLevelTech.Release(); } if( PPAttribs.m_uiExtinctionEvalMode != m_PostProcessingAttribs.m_uiExtinctionEvalMode ) { m_ptex2DEpipolarExtinctionRTV.Release(); m_ptex2DEpipolarExtinctionSRV.Release(); m_UnwarpEpipolarSctrImgTech.Release(); m_UnwarpAndRenderLuminanceTech.Release(); m_RenderCoarseUnshadowedInsctrTech.Release(); } if( PPAttribs.m_uiSingleScatteringMode != m_PostProcessingAttribs.m_uiSingleScatteringMode || PPAttribs.m_uiMultipleScatteringMode != m_PostProcessingAttribs.m_uiMultipleScatteringMode ) m_RenderCoarseUnshadowedInsctrTech.Release(); bool bRecomputeSctrCoeffs = m_PostProcessingAttribs.m_bUseCustomSctrCoeffs != PPAttribs.m_bUseCustomSctrCoeffs || m_PostProcessingAttribs.m_fAerosolDensityScale != PPAttribs.m_fAerosolDensityScale || m_PostProcessingAttribs.m_fAerosolAbsorbtionScale != PPAttribs.m_fAerosolAbsorbtionScale || PPAttribs.m_bUseCustomSctrCoeffs && ( m_PostProcessingAttribs.m_f4CustomRlghBeta != PPAttribs.m_f4CustomRlghBeta || m_PostProcessingAttribs.m_f4CustomMieBeta != PPAttribs.m_f4CustomMieBeta ); m_PostProcessingAttribs = PPAttribs; m_bUseCombinedMinMaxTexture = bUseCombinedMinMaxTexture; if( bRecomputeSctrCoeffs ) { m_ptex2DOccludedNetDensityToAtmTopSRV.Release(); m_ptex2DOccludedNetDensityToAtmTopRTV.Release(); m_ptex3DSingleScatteringSRV.Release(); m_ptex2DAmbientSkyLightRTV.Release(); m_ptex2DAmbientSkyLightSRV.Release(); ComputeScatteringCoefficients(FrameAttribs.pd3dDeviceContext); } if( !m_ptex2DCoordinateTextureRTV || !m_ptex2DCoordinateTextureSRV ) { V( CreateTextures(FrameAttribs.pd3dDevice) ); } if( !m_ptex2DMinMaxShadowMapSRV[0] && m_PostProcessingAttribs.m_bUse1DMinMaxTree ) { V( CreateMinMaxShadowMap(FrameAttribs.pd3dDevice) ); } SLightAttribs &LightAttribs = *FrameAttribs.pLightAttribs; const SCameraAttribs &CamAttribs = FrameAttribs.CameraAttribs; // Note that in fact the outermost visible screen pixels do not lie exactly on the boundary (+1 or -1), but are biased by // 0.5 screen pixel size inwards. Using these adjusted boundaries improves precision and results in // smaller number of pixels which require inscattering correction assert( LightAttribs.bIsLightOnScreen == (abs(FrameAttribs.pLightAttribs->f4LightScreenPos.x) <= 1.f - 1.f/(float)m_uiBackBufferWidth && abs(FrameAttribs.pLightAttribs->f4LightScreenPos.y) <= 1.f - 1.f/(float)m_uiBackBufferHeight) ); const auto &SMAttribs = FrameAttribs.pLightAttribs->ShadowAttribs; UpdateConstantBuffer(FrameAttribs.pd3dDeviceContext, m_pcbCameraAttribs, &CamAttribs, sizeof(CamAttribs)); //UpdateConstantBuffer(FrameAttribs.pd3dDeviceContext, m_pcbLightAttribs, &LightAttribs, sizeof(LightAttribs)); UpdateConstantBuffer(FrameAttribs.pd3dDeviceContext, m_pcbPostProcessingAttribs, &m_PostProcessingAttribs, sizeof(m_PostProcessingAttribs)); // Set constant buffers that will be used by all pixel shaders and compute shader ID3D11Buffer *pCBs[] = {m_pcbPostProcessingAttribs, m_pcbMediaAttribs, m_pcbCameraAttribs, FrameAttribs.pcbLightAttribs}; FrameAttribs.pd3dDeviceContext->VSSetConstantBuffers(0, _countof(pCBs), pCBs); FrameAttribs.pd3dDeviceContext->GSSetConstantBuffers(0, _countof(pCBs), pCBs); FrameAttribs.pd3dDeviceContext->PSSetConstantBuffers(0, _countof(pCBs), pCBs); FrameAttribs.pd3dDeviceContext->CSSetConstantBuffers(0, _countof(pCBs), pCBs); ID3D11SamplerState *pSamplers[] = { m_psamLinearClamp, m_psamLinearBorder0, m_psamComparison, m_psamPointClamp }; FrameAttribs.pd3dDeviceContext->PSSetSamplers(0, _countof(pSamplers), pSamplers); FrameAttribs.pd3dDeviceContext->CSSetSamplers(0, 1, pSamplers); D3D11_VIEWPORT OrigViewPort; UINT iNumOldViewports = 1; FrameAttribs.pd3dDeviceContext->RSGetViewports(&iNumOldViewports, &OrigViewPort); if( !m_ptex2DOccludedNetDensityToAtmTopSRV ) { CreatePrecomputedOpticalDepthTexture(FrameAttribs.pd3dDevice, FrameAttribs.pd3dDeviceContext); } if( (m_PostProcessingAttribs.m_uiMultipleScatteringMode > MULTIPLE_SCTR_MODE_NONE || PPAttribs.m_uiSingleScatteringMode == SINGLE_SCTR_MODE_LUT) && !m_ptex3DSingleScatteringSRV ) { CreatePrecomputedScatteringLUT(FrameAttribs.pd3dDevice, FrameAttribs.pd3dDeviceContext); } if( m_PostProcessingAttribs.m_bAutoExposure && !m_ptex2DLowResLuminanceRTV ) { CreateLowResLuminanceTexture(FrameAttribs.pd3dDevice); } float Zero[4]={0,0,0,0}; FrameAttribs.pd3dDeviceContext->ClearRenderTargetView(FrameAttribs.pDstRTV, Zero); RenderSun(FrameAttribs); ReconstructCameraSpaceZ(FrameAttribs); if( m_PostProcessingAttribs.m_uiLightSctrTechnique == LIGHT_SCTR_TECHNIQUE_EPIPOLAR_SAMPLING ) { RenderSliceEndpoints(FrameAttribs); // Render coordinate texture and camera space z for epipolar location RenderCoordinateTexture(FrameAttribs); if( m_PostProcessingAttribs.m_uiRefinementCriterion == REFINEMENT_CRITERION_INSCTR_DIFF || m_PostProcessingAttribs.m_uiExtinctionEvalMode == EXTINCTION_EVAL_MODE_EPIPOLAR ) { RenderCoarseUnshadowedInctr(FrameAttribs); } // Refine initial ray marching samples RefineSampleLocations(FrameAttribs); // Mark all ray marching samples in stencil MarkRayMarchingSamples( FrameAttribs ); if( m_PostProcessingAttribs.m_bEnableLightShafts && m_PostProcessingAttribs.m_bUse1DMinMaxTree ) { RenderSliceUVDirAndOrig(FrameAttribs); } FrameAttribs.pd3dDeviceContext->ClearRenderTargetView(m_ptex2DInitialScatteredLightRTV, Zero); int iLastCascade = (m_PostProcessingAttribs.m_bEnableLightShafts && m_PostProcessingAttribs.m_uiCascadeProcessingMode == CASCADE_PROCESSING_MODE_MULTI_PASS) ? m_PostProcessingAttribs.m_iNumCascades - 1 : m_PostProcessingAttribs.m_iFirstCascade; for(int iCascadeInd = m_PostProcessingAttribs.m_iFirstCascade; iCascadeInd <= iLastCascade; ++iCascadeInd) { // Build min/max mip map if( m_PostProcessingAttribs.m_bEnableLightShafts && m_PostProcessingAttribs.m_bUse1DMinMaxTree ) { Build1DMinMaxMipMap(FrameAttribs, iCascadeInd); } // Perform ray marching for selected samples DoRayMarching(FrameAttribs, m_PostProcessingAttribs.m_uiShadowMapResolution, SMAttribs, iCascadeInd); } // Interpolate ray marching samples onto the rest of samples InterpolateInsctrIrradiance(FrameAttribs); const UINT uiMaxStepsAlongRayAtDepthBreak0 = min(m_PostProcessingAttribs.m_uiShadowMapResolution/4, 256); const UINT uiMaxStepsAlongRayAtDepthBreak1 = min(m_PostProcessingAttribs.m_uiShadowMapResolution/8, 128); ID3D11DepthStencilView *pDSV = m_ptex2DScreenSizeDSV; ID3D11RenderTargetView *pRTV[] = { FrameAttribs.pDstRTV }; FrameAttribs.pd3dDeviceContext->ClearDepthStencilView(pDSV, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 0, 0); if( m_PostProcessingAttribs.m_bAutoExposure ) { // Render scene luminance to low-resolution texture FrameAttribs.pd3dDeviceContext->OMSetRenderTargets(1, &m_ptex2DLowResLuminanceRTV.p, nullptr); UnwarpEpipolarScattering(FrameAttribs, true); FrameAttribs.pd3dDeviceContext->GenerateMips(m_ptex2DLowResLuminanceSRV); UpdateAverageLuminance(FrameAttribs); } // Transform inscattering irradiance from epipolar coordinates back to rectangular FrameAttribs.pd3dDeviceContext->OMSetRenderTargets(1, pRTV, pDSV); UnwarpEpipolarScattering(FrameAttribs, false); // Correct inscattering for pixels, for which no suitable interpolation sources were found if( m_PostProcessingAttribs.m_bCorrectScatteringAtDepthBreaks ) { FixInscatteringAtDepthBreaks(FrameAttribs, uiMaxStepsAlongRayAtDepthBreak0, SMAttribs, false); } if( m_PostProcessingAttribs.m_bShowSampling ) { RenderSampleLocations(FrameAttribs); } } else if(m_PostProcessingAttribs.m_uiLightSctrTechnique == LIGHT_SCTR_TECHNIQUE_BRUTE_FORCE ) { if( m_PostProcessingAttribs.m_bAutoExposure ) { // Render scene luminance to low-resolution texture FrameAttribs.pd3dDeviceContext->OMSetRenderTargets(1, &m_ptex2DLowResLuminanceRTV.p, nullptr); FixInscatteringAtDepthBreaks(FrameAttribs, m_PostProcessingAttribs.m_uiShadowMapResolution, SMAttribs, true); FrameAttribs.pd3dDeviceContext->GenerateMips(m_ptex2DLowResLuminanceSRV); UpdateAverageLuminance(FrameAttribs); } FrameAttribs.pd3dDeviceContext->ClearDepthStencilView(m_ptex2DScreenSizeDSV, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 0, 0); FrameAttribs.pd3dDeviceContext->OMSetRenderTargets(1, &FrameAttribs.pDstRTV, m_ptex2DScreenSizeDSV); FixInscatteringAtDepthBreaks(FrameAttribs, m_PostProcessingAttribs.m_uiShadowMapResolution, SMAttribs, false); } } HRESULT CLightSctrPostProcess :: CreateTextures(ID3D11Device* pd3dDevice) { HRESULT hr; D3D11_TEXTURE2D_DESC CoordinateTexDesc = { m_PostProcessingAttribs.m_uiMaxSamplesInSlice, //UINT Width; m_PostProcessingAttribs.m_uiNumEpipolarSlices, //UINT Height; 1, //UINT MipLevels; 1, //UINT ArraySize; DXGI_FORMAT_R32G32_FLOAT, //DXGI_FORMAT Format; {1,0}, //DXGI_SAMPLE_DESC SampleDesc; D3D11_USAGE_DEFAULT, //D3D11_USAGE Usage; D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET, //UINT BindFlags; 0, //UINT CPUAccessFlags; 0, //UINT MiscFlags; }; { CComPtr ptex2DCoordinateTexture; // Create 2-D texture, shader resource and target view buffers on the device V_RETURN( pd3dDevice->CreateTexture2D( &CoordinateTexDesc, NULL, &ptex2DCoordinateTexture) ); V_RETURN( pd3dDevice->CreateShaderResourceView( ptex2DCoordinateTexture, NULL, &m_ptex2DCoordinateTextureSRV) ); V_RETURN( pd3dDevice->CreateRenderTargetView( ptex2DCoordinateTexture, NULL, &m_ptex2DCoordinateTextureRTV) ); } { m_ptex2DSliceEndpointsSRV.Release(); m_ptex2DSliceEndpointsRTV.Release(); D3D11_TEXTURE2D_DESC InterpolationSourceTexDesc = CoordinateTexDesc; InterpolationSourceTexDesc.Width = m_PostProcessingAttribs.m_uiNumEpipolarSlices; InterpolationSourceTexDesc.Height = 1; InterpolationSourceTexDesc.Format = DXGI_FORMAT_R32G32B32A32_FLOAT; InterpolationSourceTexDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET; CComPtr ptex2DSliceEndpoints; // Create 2-D texture, shader resource and target view buffers on the device V_RETURN( pd3dDevice->CreateTexture2D( &InterpolationSourceTexDesc, NULL, &ptex2DSliceEndpoints) ); V_RETURN( pd3dDevice->CreateShaderResourceView( ptex2DSliceEndpoints, NULL, &m_ptex2DSliceEndpointsSRV) ); V_RETURN( pd3dDevice->CreateRenderTargetView( ptex2DSliceEndpoints, NULL, &m_ptex2DSliceEndpointsRTV) ); } { m_ptex2DInterpolationSourcesSRV.Release(); m_ptex2DInterpolationSourcesUAV.Release(); D3D11_TEXTURE2D_DESC InterpolationSourceTexDesc = CoordinateTexDesc; InterpolationSourceTexDesc.Format = DXGI_FORMAT_R16G16_UINT; InterpolationSourceTexDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_UNORDERED_ACCESS; CComPtr ptex2DInterpolationSource; // Create 2-D texture, shader resource and target view buffers on the device V_RETURN( pd3dDevice->CreateTexture2D( &InterpolationSourceTexDesc, NULL, &ptex2DInterpolationSource) ); V_RETURN( pd3dDevice->CreateShaderResourceView( ptex2DInterpolationSource, NULL, &m_ptex2DInterpolationSourcesSRV) ); V_RETURN( pd3dDevice->CreateUnorderedAccessView( ptex2DInterpolationSource, NULL, &m_ptex2DInterpolationSourcesUAV) ); } { m_ptex2DEpipolarCamSpaceZSRV.Release(); m_ptex2DEpipolarCamSpaceZRTV.Release(); D3D11_TEXTURE2D_DESC EpipolarCamSpaceZTexDesc = CoordinateTexDesc; EpipolarCamSpaceZTexDesc.Format = DXGI_FORMAT_R32_FLOAT; EpipolarCamSpaceZTexDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET; CComPtr ptex2DEpipolarCamSpace; // Create 2-D texture, shader resource and target view buffers on the device V_RETURN( pd3dDevice->CreateTexture2D( &EpipolarCamSpaceZTexDesc, NULL, &ptex2DEpipolarCamSpace) ); V_RETURN( pd3dDevice->CreateShaderResourceView( ptex2DEpipolarCamSpace, NULL, &m_ptex2DEpipolarCamSpaceZSRV) ); V_RETURN( pd3dDevice->CreateRenderTargetView( ptex2DEpipolarCamSpace, NULL, &m_ptex2DEpipolarCamSpaceZRTV) ); } { m_ptex2DEpipolarInscatteringSRV.Release(); m_ptex2DEpipolarInscatteringRTV.Release(); D3D11_TEXTURE2D_DESC ScatteredLightTexDesc = CoordinateTexDesc; // R8G8B8A8_UNORM texture does not provide sufficient precision which causes // interpolation artifacts especially noticeable in low intensity regions ScatteredLightTexDesc.Format = DXGI_FORMAT_R16G16B16A16_FLOAT; CComPtr ptex2DEpipolarInscattering; // Create 2-D texture, shader resource and target view buffers on the device V_RETURN( pd3dDevice->CreateTexture2D( &ScatteredLightTexDesc, NULL, &ptex2DEpipolarInscattering) ); V_RETURN( pd3dDevice->CreateShaderResourceView( ptex2DEpipolarInscattering, NULL, &m_ptex2DEpipolarInscatteringSRV) ); V_RETURN( pd3dDevice->CreateRenderTargetView( ptex2DEpipolarInscattering, NULL, &m_ptex2DEpipolarInscatteringRTV) ); m_ptex2DInitialScatteredLightSRV.Release(); m_ptex2DInitialScatteredLightRTV.Release(); CComPtr ptex2DInitialScatteredLight; // Create 2-D texture, shader resource and target view buffers on the device V_RETURN( pd3dDevice->CreateTexture2D( &ScatteredLightTexDesc, NULL, &ptex2DInitialScatteredLight) ); V_RETURN( pd3dDevice->CreateShaderResourceView( ptex2DInitialScatteredLight, NULL, &m_ptex2DInitialScatteredLightSRV) ); V_RETURN( pd3dDevice->CreateRenderTargetView( ptex2DInitialScatteredLight, NULL, &m_ptex2DInitialScatteredLightRTV) ); // Release extinction texture so that it is re-created when first needed m_ptex2DEpipolarExtinctionSRV.Release(); m_ptex2DEpipolarExtinctionRTV.Release(); } { m_ptex2DEpipolarImageDSV.Release(); D3D11_TEXTURE2D_DESC EpipolarDeptTexDesc = CoordinateTexDesc; EpipolarDeptTexDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT; EpipolarDeptTexDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL; CComPtr ptex2DEpipolarImage; V_RETURN( pd3dDevice->CreateTexture2D( &EpipolarDeptTexDesc, NULL, &ptex2DEpipolarImage) ); V_RETURN( pd3dDevice->CreateDepthStencilView( ptex2DEpipolarImage, NULL, &m_ptex2DEpipolarImageDSV) ); } { m_ptex2DSliceUVDirAndOriginSRV.Release(); m_ptex2DSliceUVDirAndOriginRTV.Release(); } return S_OK; } HRESULT CLightSctrPostProcess :: CreateMinMaxShadowMap(ID3D11Device* pd3dDevice) { D3D11_TEXTURE2D_DESC MinMaxShadowMapTexDesc = { // Min/max shadow map does not contain finest resolution level of the shadow map m_PostProcessingAttribs.m_uiMinMaxShadowMapResolution, //UINT Width; m_PostProcessingAttribs.m_uiNumEpipolarSlices, //UINT Height; 1, //UINT MipLevels; 1, //UINT ArraySize; m_PostProcessingAttribs.m_bIs32BitMinMaxMipMap ? DXGI_FORMAT_R32G32_FLOAT : DXGI_FORMAT_R16G16_UNORM, //DXGI_FORMAT Format; {1,0}, //DXGI_SAMPLE_DESC SampleDesc; D3D11_USAGE_DEFAULT, //D3D11_USAGE Usage; D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET, //UINT BindFlags; 0, //UINT CPUAccessFlags; 0, //UINT MiscFlags; }; if( m_bUseCombinedMinMaxTexture ) { MinMaxShadowMapTexDesc.Height *= (m_PostProcessingAttribs.m_iNumCascades - m_PostProcessingAttribs.m_iFirstCascade); } HRESULT hr; for(int i=0; i < 2; ++i) { m_ptex2DMinMaxShadowMapSRV[i].Release(); m_ptex2DMinMaxShadowMapRTV[i].Release(); CComPtr ptex2DMinMaxShadowMap; // Create 2-D texture, shader resource and target view buffers on the device V_RETURN( pd3dDevice->CreateTexture2D( &MinMaxShadowMapTexDesc, NULL, &ptex2DMinMaxShadowMap) ); V_RETURN( pd3dDevice->CreateShaderResourceView( ptex2DMinMaxShadowMap, NULL, &m_ptex2DMinMaxShadowMapSRV[i]) ); V_RETURN( pd3dDevice->CreateRenderTargetView( ptex2DMinMaxShadowMap, NULL, &m_ptex2DMinMaxShadowMapRTV[i]) ); } return S_OK; } D3DXVECTOR2 exp(const D3DXVECTOR2 &fX){ return D3DXVECTOR2(exp(fX.x), exp(fX.y)); } D3DXVECTOR3 exp(const D3DXVECTOR3 &fX){ return D3DXVECTOR3(exp(fX.x), exp(fX.y), exp(fX.z)); } D3DXVECTOR2 operator * (const D3DXVECTOR2 &fX, const D3DXVECTOR2 &fY){ return D3DXVECTOR2(fX.x*fY.x, fX.y*fY.y); } D3DXVECTOR3 operator * (const D3DXVECTOR3 &fX, const D3DXVECTOR3 &fY){ return D3DXVECTOR3(fX.x*fY.x, fX.y*fY.y, fX.z*fY.z); } D3DXVECTOR2 operator / (const D3DXVECTOR2 &fX, const D3DXVECTOR2 &fY){ return D3DXVECTOR2(fX.x/fY.x, fX.y/fY.y); } // fCosChi = Pi/2 D3DXVECTOR2 ChapmanOrtho(const D3DXVECTOR2 &f2x) { static const float fConst = static_cast( sqrt(D3DX_PI / 2) ); D3DXVECTOR2 f2SqrtX = D3DXVECTOR2( sqrt(f2x.x), sqrt(f2x.y) ); return fConst * ( D3DXVECTOR2(1.f,1.f) / (2.f * f2SqrtX) + f2SqrtX ); } // |fCosChi| < Pi/2 D3DXVECTOR2 f2ChapmanRising(const D3DXVECTOR2 &f2X, float fCosChi) { D3DXVECTOR2 f2ChOrtho = ChapmanOrtho(f2X); return f2ChOrtho / ((f2ChOrtho-D3DXVECTOR2(1,1))*fCosChi + D3DXVECTOR2(1,1)); } D3DXVECTOR2 GetDensityIntegralFromChapmanFunc(float fHeightAboveSurface, const D3DXVECTOR3 &f3EarthCentreToPointDir, const D3DXVECTOR3 &f3RayDir, const SAirScatteringAttribs &SctrMediaAttribs) { // Note: there is no intersection test with the Earth. However, // optical depth through the Earth is large, which effectively // occludes the light float fCosChi = D3DXVec3Dot(&f3EarthCentreToPointDir, &f3RayDir); D3DXVECTOR2 f2x = (fHeightAboveSurface + SctrMediaAttribs.fEarthRadius) * D3DXVECTOR2(1.f / SctrMediaAttribs.f2ParticleScaleHeight.x, 1.f / SctrMediaAttribs.f2ParticleScaleHeight.y); D3DXVECTOR2 f2VerticalAirMass = SctrMediaAttribs.f2ParticleScaleHeight * exp(-D3DXVECTOR2(fHeightAboveSurface,fHeightAboveSurface)/SctrMediaAttribs.f2ParticleScaleHeight); if( fCosChi >= 0.f ) { return f2VerticalAirMass * f2ChapmanRising(f2x, fCosChi); } else { float fSinChi = sqrt(1.f - fCosChi*fCosChi); float fh0 = (fHeightAboveSurface + SctrMediaAttribs.fEarthRadius) * fSinChi - SctrMediaAttribs.fEarthRadius; D3DXVECTOR2 f2VerticalAirMass0 = SctrMediaAttribs.f2ParticleScaleHeight * exp(-D3DXVECTOR2(fh0,fh0)/SctrMediaAttribs.f2ParticleScaleHeight); D3DXVECTOR2 f2x0 = D3DXVECTOR2(fh0 + SctrMediaAttribs.fEarthRadius,fh0 + SctrMediaAttribs.fEarthRadius)/SctrMediaAttribs.f2ParticleScaleHeight; D3DXVECTOR2 f2ChOrtho_x0 = ChapmanOrtho(f2x0); D3DXVECTOR2 f2Ch = f2ChapmanRising(f2x, -fCosChi); return f2VerticalAirMass0 * (2.f * f2ChOrtho_x0) - f2VerticalAirMass*f2Ch; } } void CLightSctrPostProcess :: ComputeSunColor( const D3DXVECTOR3 &vDirectionOnSun, const D3DXVECTOR4 &f4ExtraterrestrialSunColor, D3DXVECTOR4 &f4SunColorAtGround, D3DXVECTOR4 &f4AmbientLight) { // Compute the ambient light values float zenithFactor = min( max(vDirectionOnSun.y, 0.0f), 1.0f); f4AmbientLight.x = zenithFactor*0.15f; f4AmbientLight.y = zenithFactor*0.1f; f4AmbientLight.z = max(0.005f, zenithFactor*0.25f); f4AmbientLight.w = 0.0f; D3DXVECTOR2 f2NetParticleDensityToAtmTop = GetDensityIntegralFromChapmanFunc(0, D3DXVECTOR3(0,1,0), vDirectionOnSun, m_MediaParams); D3DXVECTOR3 f3RlghExtCoeff; D3DXVec3Maximize(&f3RlghExtCoeff, (D3DXVECTOR3*)&m_MediaParams.f4RayleighExtinctionCoeff, &D3DXVECTOR3(1e-8f,1e-8f,1e-8f)); D3DXVECTOR3 f3RlghOpticalDepth = f3RlghExtCoeff * f2NetParticleDensityToAtmTop.x; D3DXVECTOR3 f3MieExtCoeff; D3DXVec3Maximize(&f3MieExtCoeff, (D3DXVECTOR3*)&m_MediaParams.f4MieExtinctionCoeff, &D3DXVECTOR3(1e-8f,1e-8f,1e-8f)); D3DXVECTOR3 f3MieOpticalDepth = f3MieExtCoeff * f2NetParticleDensityToAtmTop.y; D3DXVECTOR3 f3TotalExtinction = exp( -(f3RlghOpticalDepth + f3MieOpticalDepth ) ); const float fEarthReflectance = 0.1f;// See [BN08] (D3DXVECTOR3&)f4SunColorAtGround = ((D3DXVECTOR3&)f4ExtraterrestrialSunColor) * f3TotalExtinction * fEarthReflectance; } void CLightSctrPostProcess :: ComputeScatteringCoefficients(ID3D11DeviceContext *pDeviceCtx) { // For details, see "A practical Analytic Model for Daylight" by Preetham & Hoffman, p.23 // Wave lengths // [BN08] follows [REK04] and gives the following values for Rayleigh scattering coefficients: // RayleighBetha(lambda = (680nm, 550nm, 440nm) ) = (5.8, 13.5, 33.1)e-6 static const double dWaveLengths[] = { 680e-9, // red 550e-9, // green 440e-9 // blue }; // Calculate angular and total scattering coefficients for Rayleigh scattering: { D3DXVECTOR4 &f4AngularRayleighSctrCoeff = m_MediaParams.f4AngularRayleighSctrCoeff; D3DXVECTOR4 &f4TotalRayleighSctrCoeff = m_MediaParams.f4TotalRayleighSctrCoeff; D3DXVECTOR4 &f4RayleighExtinctionCoeff = m_MediaParams.f4RayleighExtinctionCoeff; double n = 1.0003; // - Refractive index of air in the visible spectrum double N = 2.545e+25; // - Number of molecules per unit volume double Pn = 0.035; // - Depolarization factor for air which exoresses corrections // due to anisotropy of air molecules double dRayleighConst = 8.0*D3DX_PI*D3DX_PI*D3DX_PI * (n*n - 1.0) * (n*n - 1.0) / (3.0 * N) * (6.0 + 3.0*Pn) / (6.0 - 7.0*Pn); for(int WaveNum = 0; WaveNum < 3; WaveNum++) { double dSctrCoeff; if( m_PostProcessingAttribs.m_bUseCustomSctrCoeffs ) dSctrCoeff = f4TotalRayleighSctrCoeff[WaveNum] = m_PostProcessingAttribs.m_f4CustomRlghBeta[WaveNum]; else { double Lambda2 = dWaveLengths[WaveNum] * dWaveLengths[WaveNum]; double Lambda4 = Lambda2 * Lambda2; dSctrCoeff = dRayleighConst / Lambda4; // Total Rayleigh scattering coefficient is the integral of angular scattering coefficient in all directions f4TotalRayleighSctrCoeff[WaveNum] = static_cast( dSctrCoeff ); } // Angular scattering coefficient is essentially volumetric scattering coefficient multiplied by the // normalized phase function // p(Theta) = 3/(16*Pi) * (1 + cos^2(Theta)) // f4AngularRayleighSctrCoeff contains all the terms exepting 1 + cos^2(Theta): f4AngularRayleighSctrCoeff[WaveNum] = static_cast( 3.0 / (16.0*D3DX_PI) * dSctrCoeff ); // f4AngularRayleighSctrCoeff[WaveNum] = f4TotalRayleighSctrCoeff[WaveNum] * p(Theta) } // Air molecules do not absorb light, so extinction coefficient is only caused by out-scattering f4RayleighExtinctionCoeff = f4TotalRayleighSctrCoeff; } // Calculate angular and total scattering coefficients for Mie scattering: { D3DXVECTOR4 &f4AngularMieSctrCoeff = m_MediaParams.f4AngularMieSctrCoeff; D3DXVECTOR4 &f4TotalMieSctrCoeff = m_MediaParams.f4TotalMieSctrCoeff; D3DXVECTOR4 &f4MieExtinctionCoeff = m_MediaParams.f4MieExtinctionCoeff; if( m_PostProcessingAttribs.m_bUseCustomSctrCoeffs ) { f4TotalMieSctrCoeff = m_PostProcessingAttribs.m_f4CustomMieBeta * m_PostProcessingAttribs.m_fAerosolDensityScale; } else { const bool bUsePreethamMethod = false; if( bUsePreethamMethod ) { // Values for K came from the table 2 in the "A practical Analytic Model // for Daylight" by Preetham & Hoffman, p.28 double K[] = { 0.68455, // K[650nm] 0.678781, // K[570nm] (0.668532+0.669765)/2.0 // (K[470nm]+K[480nm])/2 }; assert( m_MediaParams.fTurbidity >= 1.f ); // Beta is an Angstrom's turbidity coefficient and is approximated by: //float beta = 0.04608365822050f * m_fTurbidity - 0.04586025928522f; ??????? double c = (0.6544*m_MediaParams.fTurbidity - 0.6510)*1E-16; // concentration factor const double v = 4; // Junge's exponent double dTotalMieBetaTerm = 0.434 * c * D3DX_PI * pow(2.0*D3DX_PI, v-2); for(int WaveNum = 0; WaveNum < 3; WaveNum++) { double Lambdav_minus_2 = pow( dWaveLengths[WaveNum], v-2); double dTotalMieSctrCoeff = dTotalMieBetaTerm * K[WaveNum] / Lambdav_minus_2; f4TotalMieSctrCoeff[WaveNum] = static_cast( dTotalMieSctrCoeff ); } //AtmScatteringAttribs.f4AngularMieSctrCoeff *= 0.02f; //AtmScatteringAttribs.f4TotalMieSctrCoeff *= 0.02f; } else { // [BN08] uses the following value (independent of wavelength) for Mie scattering coefficient: 2e-5 // For g=0.76 and MieBetha=2e-5 [BN08] was able to reproduce the same luminance as given by the // reference CIE sky light model const float fMieBethaBN08 = 2e-5f * m_PostProcessingAttribs.m_fAerosolDensityScale; m_MediaParams.f4TotalMieSctrCoeff = D3DXVECTOR4(fMieBethaBN08, fMieBethaBN08, fMieBethaBN08, 0); } } for(int WaveNum = 0; WaveNum < 3; WaveNum++) { // Normalized to unity Cornette-Shanks phase function has the following form: // F(theta) = 1/(4*PI) * 3*(1-g^2) / (2*(2+g^2)) * (1+cos^2(theta)) / (1 + g^2 - 2g*cos(theta))^(3/2) // The angular scattering coefficient is the volumetric scattering coefficient multiplied by the phase // function. 1/(4*PI) is baked into the f4AngularMieSctrCoeff, the other terms are baked into f4CS_g f4AngularMieSctrCoeff[WaveNum] = f4TotalMieSctrCoeff[WaveNum] / static_cast(4.0 * D3DX_PI); // [BN08] also uses slight absorption factor which is 10% of scattering f4MieExtinctionCoeff[WaveNum] = f4TotalMieSctrCoeff[WaveNum] * (1.f + m_PostProcessingAttribs.m_fAerosolAbsorbtionScale); } } { // For g=0.76 and MieBetha=2e-5 [BN08] was able to reproduce the same luminance as is given by the // reference CIE sky light model // Cornette phase function (see Nishita et al. 93): // F(theta) = 1/(4*PI) * 3*(1-g^2) / (2*(2+g^2)) * (1+cos^2(theta)) / (1 + g^2 - 2g*cos(theta))^(3/2) // 1/(4*PI) is baked into the f4AngularMieSctrCoeff D3DXVECTOR4 &f4CS_g = m_MediaParams.f4CS_g; float f_g = m_MediaParams.m_fAerosolPhaseFuncG; f4CS_g.x = 3*(1.f - f_g*f_g) / ( 2*(2.f + f_g*f_g) ); f4CS_g.y = 1.f + f_g*f_g; f4CS_g.z = -2.f*f_g; f4CS_g.w = 1.f; } m_MediaParams.f4TotalExtinctionCoeff = m_MediaParams.f4RayleighExtinctionCoeff + m_MediaParams.f4MieExtinctionCoeff; if( pDeviceCtx && m_pcbMediaAttribs ) { pDeviceCtx->UpdateSubresource(m_pcbMediaAttribs, 0, NULL, &m_MediaParams, 0, 0); } } void CLightSctrPostProcess :: RenderSun(SFrameAttribs &FrameAttribs) { if( FrameAttribs.pLightAttribs->f4LightScreenPos.w <= 0 ) return; if( !m_RenderSunTech.IsValid() ) { m_RenderSunTech.SetDeviceAndContext(FrameAttribs.pd3dDevice, FrameAttribs.pd3dDeviceContext); m_RenderSunTech.CreateVGPShadersFromFile( m_strEffectPath, "SunVS", nullptr, "SunPS", nullptr ); m_RenderSunTech.SetDS( m_pEnableDepthCmpEqDS ); m_RenderSunTech.SetRS( m_pSolidFillNoCullRS ); m_RenderSunTech.SetBS( m_pDefaultBS ); } FrameAttribs.pd3dDeviceContext->OMSetRenderTargets(1, &FrameAttribs.ptex2DSrcColorBufferRTV, FrameAttribs.ptex2DSrcDepthBufferDSV); RenderQuad( FrameAttribs.pd3dDeviceContext, m_RenderSunTech); FrameAttribs.pd3dDeviceContext->OMSetRenderTargets(0, NULL, NULL); } void CLightSctrPostProcess :: CreateAmbientSkyLightTexture(ID3D11Device *pDevice, ID3D11DeviceContext *pContext) { CRenderTechnique PrecomputeAmbientSkyLightTech; PrecomputeAmbientSkyLightTech.SetDeviceAndContext(pDevice, pContext); CD3DShaderMacroHelper Macros; Macros.AddShaderMacro( "NUM_RANDOM_SPHERE_SAMPLES", sm_iNumRandomSamplesOnSphere ); Macros.Finalize(); PrecomputeAmbientSkyLightTech.CreatePixelShaderFromFile( m_strEffectPath, "PrecomputeAmbientSkyLightPS", Macros ); PrecomputeAmbientSkyLightTech.SetVS( m_pGenerateScreenSizeQuadVS ); PrecomputeAmbientSkyLightTech.SetDS( m_pDisableDepthTestDS, 0 ); PrecomputeAmbientSkyLightTech.SetRS( m_pSolidFillNoCullRS ); PrecomputeAmbientSkyLightTech.SetBS( m_pDefaultBS ); D3D11_TEXTURE2D_DESC AmbientSkyLightTexDesc = { sm_iAmbientSkyLightTexDim, //UINT Width; 1, //UINT Height; 1, //UINT MipLevels; 1, //UINT ArraySize; DXGI_FORMAT_R16G16B16A16_FLOAT, //DXGI_FORMAT Format; {1,0}, //DXGI_SAMPLE_DESC SampleDesc; D3D11_USAGE_DEFAULT, //D3D11_USAGE Usage; D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET, //UINT BindFlags; 0, //UINT CPUAccessFlags; 0, //UINT MiscFlags; }; m_ptex2DAmbientSkyLightSRV.Release(); m_ptex2DAmbientSkyLightRTV.Release(); CComPtr ptex2DAmbientSkyLight; // Create 2-D texture, shader resource and target view buffers on the device HRESULT hr; V( pDevice->CreateTexture2D( &AmbientSkyLightTexDesc, NULL, &ptex2DAmbientSkyLight) ); V( pDevice->CreateShaderResourceView( ptex2DAmbientSkyLight, NULL, &m_ptex2DAmbientSkyLightSRV) ); V( pDevice->CreateRenderTargetView( ptex2DAmbientSkyLight, NULL, &m_ptex2DAmbientSkyLightRTV) ); CComPtr pOrigRTV; CComPtr pOrigDSV; pContext->OMGetRenderTargets(1, &pOrigRTV, &pOrigDSV); D3D11_VIEWPORT OrigViewPort; UINT iNumOldViewports = 1; pContext->RSGetViewports(&iNumOldViewports, &OrigViewPort); ID3D11Buffer *pCBs[] = {m_pcbPostProcessingAttribs, m_pcbMediaAttribs}; pContext->VSSetConstantBuffers(0, _countof(pCBs), pCBs); pContext->PSSetConstantBuffers(0, _countof(pCBs), pCBs); ID3D11SamplerState *pSamplers[] = { m_psamLinearClamp, m_psamLinearBorder0, m_psamComparison, m_psamPointClamp }; pContext->PSSetSamplers(0, _countof(pSamplers), pSamplers); if( !m_ptex2DOccludedNetDensityToAtmTopSRV ) { CreatePrecomputedOpticalDepthTexture(pDevice, pContext); } if( !m_ptex3DSingleScatteringSRV ) { CreatePrecomputedScatteringLUT(pDevice, pContext); } ID3D11ShaderResourceView *pSRVs[2] = {m_ptex3DMultipleScatteringSRV, m_ptex2DSphereRandomSamplingSRV}; pContext->PSSetShaderResources(0, _countof(pSRVs), pSRVs); pContext->OMSetRenderTargets(1, &m_ptex2DAmbientSkyLightRTV.p, nullptr); RenderQuad( pContext, PrecomputeAmbientSkyLightTech, sm_iAmbientSkyLightTexDim, 1); pContext->RSSetViewports(iNumOldViewports, &OrigViewPort); pContext->OMSetRenderTargets(1, &pOrigRTV.p, pOrigDSV); } ID3D11ShaderResourceView* CLightSctrPostProcess :: GetAmbientSkyLightSRV(ID3D11Device *pDevice, ID3D11DeviceContext *pContext) { if( !m_ptex2DAmbientSkyLightSRV ) { CreateAmbientSkyLightTexture(pDevice, pContext); } return m_ptex2DAmbientSkyLightSRV; } ================================================ FILE: LightSctrPostProcess.h ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #pragma once #include #include #include "RenderTechnique.h" #include "structures.fxh" struct SFrameAttribs { ID3D11Device *pd3dDevice; ID3D11DeviceContext *pd3dDeviceContext; double dElapsedTime; SLightAttribs *pLightAttribs; ID3D11Buffer *pcbLightAttribs; SCameraAttribs CameraAttribs; ID3D11ShaderResourceView *ptex2DSrcColorBufferSRV; ID3D11RenderTargetView *ptex2DSrcColorBufferRTV; ID3D11DepthStencilView *ptex2DSrcDepthBufferDSV; ID3D11ShaderResourceView *ptex2DSrcDepthBufferSRV; ID3D11ShaderResourceView *ptex2DShadowMapSRV; ID3D11RenderTargetView *pDstRTV; }; #undef float4 #undef float3 #undef float2 #include class CLightSctrPostProcess { public: CLightSctrPostProcess(); ~CLightSctrPostProcess(); HRESULT OnCreateDevice(ID3D11Device* in_pd3dDevice, ID3D11DeviceContext *in_pd3dDeviceContext); void OnDestroyDevice(); HRESULT OnResizedSwapChain(ID3D11Device* pd3dDevice, UINT uiBackBufferWidth, UINT uiBackBufferHeight); void PerformPostProcessing(SFrameAttribs &FrameAttribs, SPostProcessingAttribs &PPAttribs); void ComputeSunColor(const D3DXVECTOR3 &vDirectionOnSun, const D3DXVECTOR4 &f4ExtraterrestrialSunColor, D3DXVECTOR4 &f4SunColorAtGround, D3DXVECTOR4 &f4AmbientLight); void RenderSun(SFrameAttribs &FrameAttribs); ID3D11Buffer *GetMediaAttribsCB(){return m_pcbMediaAttribs;} ID3D11ShaderResourceView* GetPrecomputedNetDensitySRV(){return m_ptex2DOccludedNetDensityToAtmTopSRV;} ID3D11ShaderResourceView* GetAmbientSkyLightSRV(ID3D11Device *pDevice, ID3D11DeviceContext *pContext); private: void ReconstructCameraSpaceZ(SFrameAttribs &FrameAttribs); void RenderSliceEndpoints(SFrameAttribs &FrameAttribs); void RenderCoordinateTexture(SFrameAttribs &FrameAttribs); void RenderCoarseUnshadowedInctr(SFrameAttribs &FrameAttribs); void RefineSampleLocations(SFrameAttribs &FrameAttribs); void MarkRayMarchingSamples(SFrameAttribs &FrameAttribs); void RenderSliceUVDirAndOrig(SFrameAttribs &FrameAttribs); void Build1DMinMaxMipMap(SFrameAttribs &FrameAttribs, int iCascadeIndex); void DoRayMarching(SFrameAttribs &FrameAttribs, UINT uiMaxStepsAlongRay, const SShadowMapAttribs &SMAttribs, int iCascadeIndex); void InterpolateInsctrIrradiance(SFrameAttribs &FrameAttribs); void UnwarpEpipolarScattering(SFrameAttribs &FrameAttribs, bool bRenderLuminance); void UpdateAverageLuminance(SFrameAttribs &FrameAttribs); void FixInscatteringAtDepthBreaks(SFrameAttribs &FrameAttribs, UINT uiMaxStepsAlongRay, const SShadowMapAttribs &SMAttribs, bool bRenderLuminance); void RenderSampleLocations(SFrameAttribs &FrameAttribs); HRESULT CreatePrecomputedOpticalDepthTexture(ID3D11Device *pDevice, ID3D11DeviceContext *pContext); HRESULT CreatePrecomputedScatteringLUT(ID3D11Device *pDevice, ID3D11DeviceContext *pContext); void CreateRandomSphereSamplingTexture(ID3D11Device *pDevice); void CreateLowResLuminanceTexture(ID3D11Device *pDevice); void CreateAmbientSkyLightTexture(ID3D11Device *pDevice, ID3D11DeviceContext *pContext); void DefineMacros(class CD3DShaderMacroHelper &Macros); SPostProcessingAttribs m_PostProcessingAttribs; bool m_bUseCombinedMinMaxTexture; UINT m_uiSampleRefinementCSThreadGroupSize; UINT m_uiSampleRefinementCSMinimumThreadGroupSize; CComPtr m_ptex2DSliceEndpointsSRV; CComPtr m_ptex2DSliceEndpointsRTV; CComPtr m_ptex2DCoordinateTextureSRV; CComPtr m_ptex2DCoordinateTextureRTV; CComPtr m_ptex2DEpipolarImageDSV; CComPtr m_ptex2DEpipolarCamSpaceZSRV; CComPtr m_ptex2DEpipolarCamSpaceZRTV; CComPtr m_ptex2DInterpolationSourcesSRV; CComPtr m_ptex2DInterpolationSourcesUAV; CComPtr m_ptex2DInitialScatteredLightSRV; CComPtr m_ptex2DInitialScatteredLightRTV; CComPtr m_ptex2DEpipolarInscatteringSRV, m_ptex2DEpipolarExtinctionSRV; CComPtr m_ptex2DEpipolarInscatteringRTV, m_ptex2DEpipolarExtinctionRTV; CComPtr m_ptex2DCameraSpaceZSRV; CComPtr m_ptex2DCameraSpaceZRTV; CComPtr m_ptex2DSliceUVDirAndOriginSRV; CComPtr m_ptex2DSliceUVDirAndOriginRTV; CComPtr m_ptex2DMinMaxShadowMapSRV[2]; CComPtr m_ptex2DMinMaxShadowMapRTV[2]; static const int sm_iNumPrecomputedHeights = 1024; static const int sm_iNumPrecomputedAngles = 1024; CComPtr m_ptex2DOccludedNetDensityToAtmTopSRV; CComPtr m_ptex2DOccludedNetDensityToAtmTopRTV; static const int sm_iPrecomputedSctrUDim = 32; static const int sm_iPrecomputedSctrVDim = 128; static const int sm_iPrecomputedSctrWDim = 64; static const int sm_iPrecomputedSctrQDim = 16; CComPtr m_ptex3DSingleScatteringSRV; CComPtr m_ptex3DHighOrderScatteringSRV; CComPtr m_ptex3DMultipleScatteringSRV; static const int sm_iNumRandomSamplesOnSphere = 128; CComPtr m_ptex2DSphereRandomSamplingSRV; HRESULT CreateTextures(ID3D11Device* pd3dDevice); HRESULT CreateMinMaxShadowMap(ID3D11Device* pd3dDevice); CComPtr m_ptex2DScreenSizeDSV; static const int sm_iLowResLuminanceMips = 7; // 64x64 CComPtr m_ptex2DLowResLuminanceRTV; CComPtr m_ptex2DLowResLuminanceSRV; CComPtr m_ptex2DAverageLuminanceRTV; CComPtr m_ptex2DAverageLuminanceSRV; static const int sm_iAmbientSkyLightTexDim = 1024; CComPtr m_ptex2DAmbientSkyLightRTV; CComPtr m_ptex2DAmbientSkyLightSRV; UINT m_uiBackBufferWidth, m_uiBackBufferHeight; LPCTSTR m_strEffectPath; CComPtr m_pGenerateScreenSizeQuadVS; CRenderTechnique m_ReconstrCamSpaceZTech; CRenderTechnique m_RendedSliceEndpointsTech; CRenderTechnique m_RendedCoordTexTech; CRenderTechnique m_RefineSampleLocationsTech; CRenderTechnique m_RenderCoarseUnshadowedInsctrTech; CRenderTechnique m_MarkRayMarchingSamplesInStencilTech; CRenderTechnique m_RenderSliceUVDirInSMTech; CRenderTechnique m_InitializeMinMaxShadowMapTech; CRenderTechnique m_ComputeMinMaxSMLevelTech; CRenderTechnique m_DoRayMarchTech[2]; // 0 - min/max optimization disabled; 1 - min/max optimization enabled CRenderTechnique m_InterpolateIrradianceTech; CRenderTechnique m_UnwarpEpipolarSctrImgTech; CRenderTechnique m_UnwarpAndRenderLuminanceTech; CRenderTechnique m_UpdateAverageLuminanceTech; CRenderTechnique m_FixInsctrAtDepthBreaksTech[4]; // bit 0: 0 - perform ray marching, 1 - perform ray marching and attenuate background // bit 1: 0 - perform tone mapping, 1 - render luminance only CRenderTechnique m_RenderSampleLocationsTech; CRenderTechnique m_RenderSunTech; CRenderTechnique m_PrecomputeSingleSctrTech; CRenderTechnique m_ComputeSctrRadianceTech; CRenderTechnique m_ComputeScatteringOrderTech; CRenderTechnique m_AddScatteringOrderTech; CComPtr m_psamLinearClamp; CComPtr m_psamLinearBorder0; CComPtr m_psamComparison; CComPtr m_psamPointClamp; CComPtr m_pEnableDepthCmpEqDS; CComPtr m_pDisableDepthTestDS; CComPtr m_pDisableDepthTestIncrStencilDS; CComPtr m_pNoDepth_StEqual_IncrStencilDS; CComPtr m_pNoDepth_StEqual_KeepStencilDS; CComPtr m_pSolidFillNoCullRS; CComPtr m_pDefaultBS, m_pAdditiveBlendBS, m_pAlphaBlendBS; void ComputeScatteringCoefficients(ID3D11DeviceContext *pDeviceCtx = NULL); const float m_fTurbidity; SAirScatteringAttribs m_MediaParams; CComPtr m_pcbCameraAttribs; CComPtr m_pcbPostProcessingAttribs; CComPtr m_pcbMediaAttribs; CComPtr m_pcbMiscParams; }; ================================================ FILE: OutdoorLightScattering.cpp ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "OutdoorLightScattering.h" #include "LightSctrPostProcess.h" #include #include "CPUTBufferDX11.h" #include // important to use the right CPUT namespace void UpdateConstantBuffer(ID3D11DeviceContext *pDeviceCtx, ID3D11Buffer *pCB, const void *pData, size_t DataSize); class CParallelLightCamera : public CPUTCamera { public: virtual void Update( float deltaSeconds=0.0f ) { mView = inverse(*GetWorldMatrix()); }; }; // Constructor //----------------------------------------------------------------------------- COutdoorLightScatteringSample::COutdoorLightScatteringSample() : mpCameraController(NULL), m_pLightController(NULL), m_uiShadowMapResolution( 1024 ), m_fCascadePartitioningFactor(0.95f), m_bEnableLightScattering(true), m_bAnimateSun(false), m_pOffscreenRenderTarget(NULL), m_pOffscreenDepth(NULL), m_fScatteringScale(0.5f), m_f4LightColor(1.f, 1.f, 1.f, 1.f), m_iGUIMode(1), m_uiBackBufferWidth(0), m_uiBackBufferHeight(0), m_uiSelectedPanelInd(0), m_CameraPos(0,0,0) { m_pLightSctrPP = new CLightSctrPostProcess; } // Destructor //----------------------------------------------------------------------------- COutdoorLightScatteringSample::~COutdoorLightScatteringSample() { Destroy(); SAFE_DELETE( m_pLightSctrPP ); } void COutdoorLightScatteringSample::Destroy() { m_EarthHemisphere.OnD3D11DestroyDevice(); ReleaseShadowMap(); ReleaseTmpBackBuffAndDepthBuff(); m_pLightSctrPP->OnDestroyDevice(); SAFE_RELEASE(m_pDirectionalLightCamera); SAFE_RELEASE(m_pDirLightOrienationCamera); SAFE_RELEASE(mpCamera); SAFE_DELETE( mpCameraController); SAFE_DELETE( m_pLightController); m_pcbLightAttribs.Release(); } // Handle keyboard events //----------------------------------------------------------------------------- CPUTEventHandledCode COutdoorLightScatteringSample::HandleKeyboardEvent(CPUTKey key) { CPUTEventHandledCode handled = CPUT_EVENT_UNHANDLED; CPUTGuiControllerDX11* pGUI = CPUTGetGuiController(); cString filename; switch(key) { case KEY_F1: ++m_iGUIMode; if( m_iGUIMode>2 ) m_iGUIMode = 0; if(m_iGUIMode==1) pGUI->SetActivePanel(CONTROL_PANEL_IDS[m_uiSelectedPanelInd]); else if(m_iGUIMode==2) pGUI->SetActivePanel(ID_HELP_TEXT_PANEL); handled = CPUT_EVENT_HANDLED; break; case KEY_ESCAPE: handled = CPUT_EVENT_HANDLED; Shutdown(); break; } if((handled == CPUT_EVENT_UNHANDLED) && mpCameraController) { handled = mpCameraController->HandleKeyboardEvent(key); } if((handled == CPUT_EVENT_UNHANDLED) && m_pLightController) { handled = m_pLightController->HandleKeyboardEvent(key); } return handled; } // Handle mouse events //----------------------------------------------------------------------------- CPUTEventHandledCode COutdoorLightScatteringSample::HandleMouseEvent(int x, int y, int wheel, CPUTMouseState state) { CPUTEventHandledCode handled = CPUT_EVENT_UNHANDLED; if( mpCameraController ) { handled = mpCameraController->HandleMouseEvent(x,y,wheel, state); } if( (handled == CPUT_EVENT_UNHANDLED) && m_pLightController ) { handled = m_pLightController->HandleMouseEvent(x,y,wheel, state); } return handled; } bool SelectColor(D3DXVECTOR4 &f4Color) { // Create an initial color from the current value COLORREF InitColor = RGB( f4Color.x * 255.f, f4Color.y * 255.f, f4Color.z * 255.f ); COLORREF CustomColors[16]; // Now create a choose color structure with the original color as the default CHOOSECOLOR ChooseColorInitStruct; ZeroMemory( &ChooseColorInitStruct, sizeof(ChooseColorInitStruct)); CPUTOSServices::GetOSServices()->GetWindowHandle( &ChooseColorInitStruct.hwndOwner ); ChooseColorInitStruct.lStructSize = sizeof(ChooseColorInitStruct); ChooseColorInitStruct.rgbResult = InitColor; ChooseColorInitStruct.Flags = CC_ANYCOLOR | CC_FULLOPEN | CC_RGBINIT; ChooseColorInitStruct.lpCustColors = CustomColors; // Display a color selection dialog box and if the user does not cancel, select a color if( ChooseColor( &ChooseColorInitStruct ) && ChooseColorInitStruct.rgbResult != 0) { // Get the new color COLORREF NewColor = ChooseColorInitStruct.rgbResult; f4Color.x = (float)(NewColor&0x0FF) / 255.f; f4Color.y = (float)((NewColor>>8)&0x0FF) / 255.f; f4Color.z = (float)((NewColor>>16)&0x0FF) / 255.f; f4Color.w = 0; return true; } return false; } // Handle any control callback events //----------------------------------------------------------------------------- void COutdoorLightScatteringSample::HandleCallbackEvent( CPUTEventID Event, CPUTControlID ControlID, CPUTControl* pControl ) { cString SelectedItem; CPUTGuiControllerDX11* pGUI = CPUTGetGuiController(); switch(ControlID) { case ID_SELECT_PANEL_COMBO: { CPUTDropdown* pDropDown = static_cast(pControl); pDropDown->GetSelectedItem(m_uiSelectedPanelInd); for(int i = 0; i < _countof(m_pSelectPanelDropDowns); ++i) m_pSelectPanelDropDowns[i]->SetSelectedItem(m_uiSelectedPanelInd+1); pGUI->SetActivePanel(CONTROL_PANEL_IDS[m_uiSelectedPanelInd]); break; } case ID_FULLSCREEN_BUTTON: CPUTToggleFullScreenMode(); pGUI->GetControl(ID_RLGH_COLOR_BTN)->SetEnable(!CPUTGetFullscreenState() && m_PPAttribs.m_bUseCustomSctrCoeffs); pGUI->GetControl(ID_MIE_COLOR_BTN)->SetEnable(!CPUTGetFullscreenState() && m_PPAttribs.m_bUseCustomSctrCoeffs); break; case ID_ENABLE_VSYNC: { CPUTCheckbox *pCheckBox = static_cast(pControl); mSyncInterval = pCheckBox->GetCheckboxState() == CPUT_CHECKBOX_CHECKED ? 1 : 0; break; } case ID_ENABLE_LIGHT_SCATTERING: { CPUTCheckbox *pCheckBox = static_cast(pControl); m_bEnableLightScattering = pCheckBox->GetCheckboxState() == CPUT_CHECKBOX_CHECKED; break; } case ID_ANIMATE_SUN: { CPUTCheckbox *pCheckBox = static_cast(pControl); m_bAnimateSun = pCheckBox->GetCheckboxState() == CPUT_CHECKBOX_CHECKED; break; } case ID_ENABLE_LIGHT_SHAFTS: { CPUTCheckbox *pCheckBox = static_cast(pControl); m_PPAttribs.m_bEnableLightShafts = pCheckBox->GetCheckboxState() == CPUT_CHECKBOX_CHECKED; pGUI->GetControl(ID_NUM_INTEGRATION_STEPS)->SetEnable(!m_PPAttribs.m_bEnableLightShafts && m_PPAttribs.m_uiSingleScatteringMode == SINGLE_SCTR_MODE_INTEGRATION); break; } case ID_LIGHT_SCTR_TECHNIQUE: { CPUTDropdown* pDropDown = static_cast(pControl); UINT uiSelectedItem; pDropDown->GetSelectedItem(uiSelectedItem); m_PPAttribs.m_uiLightSctrTechnique = uiSelectedItem; bool bIsEpipolarSampling = m_PPAttribs.m_uiLightSctrTechnique == LIGHT_SCTR_TECHNIQUE_EPIPOLAR_SAMPLING; pGUI->GetControl(ID_NUM_EPIPOLAR_SLICES)->SetEnable(bIsEpipolarSampling); pGUI->GetControl(ID_NUM_SAMPLES_IN_EPIPOLAR_SLICE)->SetEnable(bIsEpipolarSampling); pGUI->GetControl(ID_INITIAL_SAMPLE_STEP_IN_EPIPOLAR_SLICE)->SetEnable(bIsEpipolarSampling); pGUI->GetControl(ID_EPIPOLE_SAMPLING_DENSITY_FACTOR)->SetEnable(bIsEpipolarSampling); pGUI->GetControl(ID_REFINEMENT_THRESHOLD)->SetEnable(bIsEpipolarSampling); pGUI->GetControl(ID_MIN_MAX_SHADOW_MAP_OPTIMIZATION)->SetEnable(bIsEpipolarSampling); pGUI->GetControl(ID_OPTIMIZE_SAMPLE_LOCATIONS)->SetEnable(bIsEpipolarSampling); pGUI->GetControl(ID_SHOW_SAMPLING)->SetEnable(bIsEpipolarSampling); pGUI->GetControl(ID_CORRECT_SCATTERING_AT_DEPTH_BREAKS)->SetEnable(bIsEpipolarSampling); pGUI->GetControl(ID_SHOW_DEPTH_BREAKS)->SetEnable(bIsEpipolarSampling); break; } case ID_SHADOW_MAP_RESOLUTION: { CPUTDropdown* pDropDown = static_cast(pControl); UINT uiSelectedItem; pDropDown->GetSelectedItem(uiSelectedItem); // uiSelectedItem is 1-based m_uiShadowMapResolution = 512 << uiSelectedItem; CreateShadowMap(mpD3dDevice); break; } case ID_NUM_EPIPOLAR_SLICES: { CPUTSlider* pSlider = static_cast(pControl); float fSliderVal; pSlider->GetValue(fSliderVal); m_PPAttribs.m_uiNumEpipolarSlices = 1 << (int)fSliderVal; std::wstringstream NumSlicesSS; NumSlicesSS << "Epipolar slices: " << m_PPAttribs.m_uiNumEpipolarSlices; pSlider->SetText( NumSlicesSS.str().c_str() ); break; } case ID_NUM_INTEGRATION_STEPS: { CPUTSlider* pSlider = static_cast(pControl); float fSliderVal; pSlider->GetValue(fSliderVal); m_PPAttribs.m_uiInstrIntegralSteps = (int)fSliderVal; std::wstringstream NumStepsSS; NumStepsSS << "Num integration steps: " << m_PPAttribs.m_uiInstrIntegralSteps; pSlider->SetText( NumStepsSS.str().c_str() ); break; } case ID_NUM_SAMPLES_IN_EPIPOLAR_SLICE: { CPUTSlider* pNumSamplesSlider = static_cast(pControl); float fSliderVal; pNumSamplesSlider->GetValue(fSliderVal); m_PPAttribs.m_uiMaxSamplesInSlice = 1 << (int)fSliderVal; std::wstringstream NumSamplesSS; NumSamplesSS << "Total samples in slice: " << m_PPAttribs.m_uiMaxSamplesInSlice; pNumSamplesSlider->SetText( NumSamplesSS.str().c_str() ); { CPUTSlider* pInitialSamplesSlider = static_cast(pGUI->GetControl(ID_INITIAL_SAMPLE_STEP_IN_EPIPOLAR_SLICE)); unsigned long ulStartVal=0, ulEndVal, ulCurrVal; BitScanForward(&ulEndVal, m_PPAttribs.m_uiMaxSamplesInSlice / m_iMinInitialSamplesInEpipolarSlice); pInitialSamplesSlider->SetScale( (float)ulStartVal, (float)ulEndVal, ulEndVal-ulStartVal+1 ); if( m_PPAttribs.m_uiInitialSampleStepInSlice > (1u<SetText( InitialSamplesStepSS.str().c_str() ); } BitScanForward(&ulCurrVal, m_PPAttribs.m_uiInitialSampleStepInSlice); pInitialSamplesSlider->SetValue( (float)ulCurrVal ); } break; } case ID_INITIAL_SAMPLE_STEP_IN_EPIPOLAR_SLICE: { CPUTSlider* pSlider = static_cast(pControl); float fSliderVal; pSlider->GetValue(fSliderVal); m_PPAttribs.m_uiInitialSampleStepInSlice = 1 << (int)fSliderVal; std::wstringstream InitialSamplesStepSS; InitialSamplesStepSS << "Initial sample step: " << m_PPAttribs.m_uiInitialSampleStepInSlice; pSlider->SetText( InitialSamplesStepSS.str().c_str() ); break; } case ID_EPIPOLE_SAMPLING_DENSITY_FACTOR: { CPUTSlider* pSlider = static_cast(pControl); float fSliderVal; pSlider->GetValue(fSliderVal); m_PPAttribs.m_uiEpipoleSamplingDensityFactor = 1 << (int)fSliderVal; std::wstringstream EpipoleSamplingDensitySS; EpipoleSamplingDensitySS << "Epipole sampling density: " << m_PPAttribs.m_uiEpipoleSamplingDensityFactor; pSlider->SetText( EpipoleSamplingDensitySS.str().c_str() ); break; } case ID_REFINEMENT_THRESHOLD: { CPUTSlider* pSlider = static_cast(pControl); pSlider->GetValue(m_PPAttribs.m_fRefinementThreshold); break; } case ID_SHOW_SAMPLING: { CPUTCheckbox *pCheckBox = static_cast(pControl); m_PPAttribs.m_bShowSampling = pCheckBox->GetCheckboxState() == CPUT_CHECKBOX_CHECKED; break; } case ID_MIN_MAX_SHADOW_MAP_OPTIMIZATION: { CPUTCheckbox *pCheckBox = static_cast(pControl); m_PPAttribs.m_bUse1DMinMaxTree = pCheckBox->GetCheckboxState()== CPUT_CHECKBOX_CHECKED; break; } case ID_CORRECT_SCATTERING_AT_DEPTH_BREAKS: { CPUTCheckbox *pCheckBox = static_cast(pControl); m_PPAttribs.m_bCorrectScatteringAtDepthBreaks = pCheckBox->GetCheckboxState() == CPUT_CHECKBOX_CHECKED; break; } case ID_SCATTERING_SCALE: { CPUTSlider* pSlider = static_cast(pControl); pSlider->GetValue(m_fScatteringScale); break; } case ID_MIDDLE_GRAY: { CPUTSlider* pSlider = static_cast(pControl); pSlider->GetValue(m_PPAttribs.m_fMiddleGray); break; } case ID_WHITE_POINT: { CPUTSlider* pSlider = static_cast(pControl); pSlider->GetValue(m_PPAttribs.m_fWhitePoint); break; } case ID_LUM_SATURATION: { CPUTSlider* pSlider = static_cast(pControl); pSlider->GetValue(m_PPAttribs.m_fLuminanceSaturation); break; } case ID_AUTO_EXPOSURE: { CPUTCheckbox *pCheckBox = static_cast(pControl); m_PPAttribs.m_bAutoExposure = pCheckBox->GetCheckboxState() == CPUT_CHECKBOX_CHECKED; pGUI->GetControl(ID_LIGHT_ADAPTATION)->SetEnable(m_PPAttribs.m_bAutoExposure ? true : false); break; } case ID_TONE_MAPPING_MODE: { CPUTDropdown *pDropDown = static_cast(pControl); pDropDown->GetSelectedItem(m_PPAttribs.m_uiToneMappingMode); pGUI->GetControl(ID_LUM_SATURATION)->SetEnable( m_PPAttribs.m_uiToneMappingMode == TONE_MAPPING_MODE_EXP || m_PPAttribs.m_uiToneMappingMode == TONE_MAPPING_MODE_REINHARD || m_PPAttribs.m_uiToneMappingMode == TONE_MAPPING_MODE_REINHARD_MOD || m_PPAttribs.m_uiToneMappingMode == TONE_MAPPING_LOGARITHMIC || m_PPAttribs.m_uiToneMappingMode == TONE_MAPPING_ADAPTIVE_LOG ); pGUI->GetControl(ID_WHITE_POINT)->SetEnable( m_PPAttribs.m_uiToneMappingMode == TONE_MAPPING_MODE_REINHARD_MOD || m_PPAttribs.m_uiToneMappingMode == TONE_MAPPING_MODE_UNCHARTED2 || m_PPAttribs.m_uiToneMappingMode == TONE_MAPPING_LOGARITHMIC || m_PPAttribs.m_uiToneMappingMode == TONE_MAPPING_ADAPTIVE_LOG ); break; } case ID_LIGHT_ADAPTATION: { CPUTCheckbox *pCheckBox = static_cast(pControl); m_PPAttribs.m_bLightAdaptation = pCheckBox->GetCheckboxState() == CPUT_CHECKBOX_CHECKED; break; } case ID_OPTIMIZE_SAMPLE_LOCATIONS: { CPUTCheckbox *pCheckBox = static_cast(pControl); m_PPAttribs.m_bOptimizeSampleLocations = pCheckBox->GetCheckboxState() == CPUT_CHECKBOX_CHECKED; break; } case ID_SHOW_DEPTH_BREAKS: { CPUTCheckbox *pCheckBox = static_cast(pControl); m_PPAttribs.m_bShowDepthBreaks = pCheckBox->GetCheckboxState() == CPUT_CHECKBOX_CHECKED; break; } case ID_SHOW_LIGHTING_ONLY_CHECK: { CPUTCheckbox *pCheckBox = static_cast(pControl); m_PPAttribs.m_bShowLightingOnly = pCheckBox->GetCheckboxState() == CPUT_CHECKBOX_CHECKED; break; } case ID_USE_CUSTOM_SCTR_COEFFS_CHECK: { CPUTCheckbox *pCheckBox = static_cast(pControl); m_PPAttribs.m_bUseCustomSctrCoeffs = pCheckBox->GetCheckboxState() == CPUT_CHECKBOX_CHECKED; pGUI->GetControl(ID_RLGH_COLOR_BTN)->SetEnable(!CPUTGetFullscreenState() && m_PPAttribs.m_bUseCustomSctrCoeffs); pGUI->GetControl(ID_MIE_COLOR_BTN)->SetEnable(!CPUTGetFullscreenState() && m_PPAttribs.m_bUseCustomSctrCoeffs); break; } case ID_RLGH_COLOR_BTN: { const float fRlghColorScale = 5e-5f; D3DXVECTOR4 f4RlghColor = m_PPAttribs.m_f4CustomRlghBeta / fRlghColorScale; if( SelectColor(f4RlghColor ) ) { m_PPAttribs.m_f4CustomRlghBeta = f4RlghColor * fRlghColorScale; } break; } case ID_MIE_COLOR_BTN: { const float fMieColorScale = 5e-5f; D3DXVECTOR4 f4MieColor = m_PPAttribs.m_f4CustomMieBeta / fMieColorScale; if( SelectColor(f4MieColor ) ) { m_PPAttribs.m_f4CustomMieBeta = f4MieColor * fMieColorScale; } break; } case ID_AEROSOL_DENSITY_SCALE_SLIDER: { CPUTSlider* pSlider = static_cast(pControl); pSlider->GetValue(m_PPAttribs.m_fAerosolDensityScale); break; } case ID_AEROSOL_ABSORBTION_SCALE_SLIDER: { CPUTSlider* pSlider = static_cast(pControl); pSlider->GetValue(m_PPAttribs.m_fAerosolAbsorbtionScale); break; } case ID_SINGLE_SCTR_MODE_DROPDOWN: { CPUTDropdown *pDropDown = static_cast(pControl); pDropDown->GetSelectedItem(m_PPAttribs.m_uiSingleScatteringMode); pGUI->GetControl(ID_NUM_INTEGRATION_STEPS)->SetEnable(!m_PPAttribs.m_bEnableLightShafts && m_PPAttribs.m_uiSingleScatteringMode == SINGLE_SCTR_MODE_INTEGRATION); break; } case ID_MULTIPLE_SCTR_MODE_DROPDOWN: { CPUTDropdown *pDropDown = static_cast(pControl); pDropDown->GetSelectedItem(m_PPAttribs.m_uiMultipleScatteringMode); break; } case ID_NUM_CASCADES_DROPDOWN: { CPUTDropdown *pDropDown = static_cast(pControl); UINT uiSelectedItem; pDropDown->GetSelectedItem(uiSelectedItem); m_TerrainRenderParams.m_iNumShadowCascades = uiSelectedItem+1; CreateShadowMap(mpD3dDevice); m_EarthHemisphere.UpdateParams(m_TerrainRenderParams); break; } case ID_SMOOTH_SHADOWS_CHECK: { CPUTCheckbox *pCheckBox = static_cast(pControl); m_TerrainRenderParams.m_bSmoothShadows = pCheckBox->GetCheckboxState() == CPUT_CHECKBOX_CHECKED ? TRUE : FALSE; m_EarthHemisphere.UpdateParams(m_TerrainRenderParams); break; } case ID_BEST_CASCADE_SEARCH_CHECK: { CPUTCheckbox *pCheckBox = static_cast(pControl); m_TerrainRenderParams.m_bBestCascadeSearch = pCheckBox->GetCheckboxState() == CPUT_CHECKBOX_CHECKED ? TRUE : FALSE; m_EarthHemisphere.UpdateParams(m_TerrainRenderParams); break; } case ID_CASCADE_PARTITIONING_SLIDER: { CPUTSlider* pSlider = static_cast(pControl); pSlider->GetValue(m_fCascadePartitioningFactor); std::wstringstream SS; SS << "Partitioning Factor: " << m_fCascadePartitioningFactor; pSlider->SetText( SS.str().c_str() ); break; } case ID_CASCADE_PROCESSING_MODE_DROPDOWN: { CPUTDropdown *pDropDown = static_cast(pControl); pDropDown->GetSelectedItem(m_PPAttribs.m_uiCascadeProcessingMode); break; } case ID_EXTINCTION_EVAL_MODE_DROPDOWN: { CPUTDropdown *pDropDown = static_cast(pControl); pDropDown->GetSelectedItem(m_PPAttribs.m_uiExtinctionEvalMode); break; } case ID_MIN_MAX_MIP_FORMAT_DROPDOWN: { CPUTDropdown *pDropDown = static_cast(pControl); UINT uiSelectedItem; pDropDown->GetSelectedItem(uiSelectedItem); m_PPAttribs.m_bIs32BitMinMaxMipMap = (uiSelectedItem == 1); break; } case ID_REFINEMENT_CRITERION_DROPDOWN: { CPUTDropdown *pDropDown = static_cast(pControl); pDropDown->GetSelectedItem(m_PPAttribs.m_uiRefinementCriterion); break; } default: break; } } // Handle resize events //----------------------------------------------------------------------------- void COutdoorLightScatteringSample::ResizeWindow(UINT width, UINT height) { if( width == 0 || height == 0 ) return; m_uiBackBufferWidth = width; m_uiBackBufferHeight = height; // Before we can resize the swap chain, we must release any references to it. // We could have a "AssetLibrary::ReleaseSwapChainResources(), or similar. But, // Generic "release all" works, is simpler to implement/maintain, and is not performance critical. //pAssetLibrary->ReleaseTexturesAndBuffers(); CPUT_DX11::ResizeWindow( width, height ); // Resize any application-specific render targets here if( mpCamera ) { mpCamera->SetAspectRatio(((float)width)/((float)height)); mpCamera->Update(); } m_pLightSctrPP->OnResizedSwapChain(mpD3dDevice, width, height ); m_pOffscreenRenderTarget->RecreateRenderTarget(width, height); m_pOffscreenDepth->RecreateRenderTarget(width, height); } void COutdoorLightScatteringSample::ReleaseTmpBackBuffAndDepthBuff() { SAFE_DELETE(m_pOffscreenRenderTarget); SAFE_DELETE(m_pOffscreenDepth); } HRESULT COutdoorLightScatteringSample::CreateTmpBackBuffAndDepthBuff(ID3D11Device* pd3dDevice) { ReleaseTmpBackBuffAndDepthBuff(); DXGI_SWAP_CHAIN_DESC SwapChainDesc; mpSwapChain->GetDesc(&SwapChainDesc); SAFE_DELETE(m_pOffscreenRenderTarget); m_pOffscreenRenderTarget = new CPUTRenderTargetColor(); m_pOffscreenRenderTarget->CreateRenderTarget( cString( _L("OffscreenRenderTarget") ), SwapChainDesc.BufferDesc.Width, //UINT Width SwapChainDesc.BufferDesc.Height, //UINT Height // It is essential to use floating point format for back buffer // to avoid banding artifacts at low light conditions DXGI_FORMAT_R11G11B10_FLOAT); SAFE_DELETE(m_pOffscreenDepth); m_pOffscreenDepth = new CPUTRenderTargetDepth(); m_pOffscreenDepth->CreateRenderTarget( cString( _L("OffscreenDepthBuffer") ), SwapChainDesc.BufferDesc.Width, //UINT Width SwapChainDesc.BufferDesc.Height, //UINT Height DXGI_FORMAT_D32_FLOAT ); return D3D_OK; } void COutdoorLightScatteringSample::ReleaseShadowMap() { // Check for the existance of a shadow map buffer and if it exists, destroy it and set its pointer to NULL // Release the light space depth shader resource and depth stencil views m_pShadowMapDSVs.clear(); m_pShadowMapSRV.Release(); } HRESULT COutdoorLightScatteringSample::CreateShadowMap(ID3D11Device* pd3dDevice) { HRESULT hr; ReleaseShadowMap(); static const bool bIs32BitShadowMap = true; //ShadowMap D3D11_TEXTURE2D_DESC ShadowMapDesc = { m_uiShadowMapResolution, m_uiShadowMapResolution, 1, m_TerrainRenderParams.m_iNumShadowCascades, bIs32BitShadowMap ? DXGI_FORMAT_R32_TYPELESS : DXGI_FORMAT_R16_TYPELESS, {1,0}, D3D11_USAGE_DEFAULT, D3D11_BIND_SHADER_RESOURCE|D3D11_BIND_DEPTH_STENCIL, 0, 0 }; CComPtr ptex2DShadowMap; V_RETURN(pd3dDevice->CreateTexture2D(&ShadowMapDesc, NULL, &ptex2DShadowMap)); D3D11_SHADER_RESOURCE_VIEW_DESC ShadowMapSRVDesc; ZeroMemory( &ShadowMapSRVDesc, sizeof(ShadowMapSRVDesc) ); ShadowMapSRVDesc.Format = bIs32BitShadowMap ? DXGI_FORMAT_R32_FLOAT : DXGI_FORMAT_R16_UNORM; ShadowMapSRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY; ShadowMapSRVDesc.Texture2DArray.MostDetailedMip = 0; ShadowMapSRVDesc.Texture2DArray.MipLevels = 1; ShadowMapSRVDesc.Texture2DArray.FirstArraySlice = 0; ShadowMapSRVDesc.Texture2DArray.ArraySize = ShadowMapDesc.ArraySize; V_RETURN(pd3dDevice->CreateShaderResourceView(ptex2DShadowMap, &ShadowMapSRVDesc, &m_pShadowMapSRV)); D3D11_DEPTH_STENCIL_VIEW_DESC ShadowMapDSVDesc; ZeroMemory( &ShadowMapDSVDesc, sizeof(ShadowMapDSVDesc) ); ShadowMapDSVDesc.Format = bIs32BitShadowMap ? DXGI_FORMAT_D32_FLOAT : DXGI_FORMAT_D16_UNORM; ShadowMapDSVDesc.Flags = 0; ShadowMapDSVDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DARRAY; ShadowMapDSVDesc.Texture2DArray.MipSlice = 0; ShadowMapDSVDesc.Texture2DArray.ArraySize = 1; m_pShadowMapDSVs.resize(ShadowMapDesc.ArraySize); for(UINT iArrSlice=0; iArrSlice < ShadowMapDesc.ArraySize; iArrSlice++) { ShadowMapDSVDesc.Texture2DArray.FirstArraySlice = iArrSlice; V_RETURN(pd3dDevice->CreateDepthStencilView(ptex2DShadowMap, &ShadowMapDSVDesc, &m_pShadowMapDSVs[iArrSlice])); } return D3D_OK; } float COutdoorLightScatteringSample::GetSceneExtent() { return 10000; } // Handle OnCreation events //----------------------------------------------------------------------------- void COutdoorLightScatteringSample::Create() { CPUTAssetLibrary* pAssetLibrary = CPUTAssetLibrary::GetAssetLibrary(); CPUTGuiControllerDX11* pGUI = CPUTGetGuiController(); pGUI->DrawFPS(true); HRESULT hr; LPCWSTR ConfigPath = L"Default_Config.txt"; if( FAILED(ParseConfigurationFile( ConfigPath )) ) { LOG_ERROR(_T("Failed to load config file %s"), ConfigPath ); return; } /* * Initialize GUI */ static_assert( _countof(m_pSelectPanelDropDowns) == _countof(CONTROL_PANEL_IDS), "Incorrect size of m_pSelectPanelDropDowns" ); for(int iPanel=0; iPanel < _countof(CONTROL_PANEL_IDS); ++iPanel) { pGUI->CreateDropdown( L"Controls: basic", ID_SELECT_PANEL_COMBO, CONTROL_PANEL_IDS[iPanel], &m_pSelectPanelDropDowns[iPanel]); m_pSelectPanelDropDowns[iPanel]->AddSelectionItem( L"Controls: additional" ); m_pSelectPanelDropDowns[iPanel]->AddSelectionItem( L"Controls: tone mapping" ); // SelectedItem is 1-based m_pSelectPanelDropDowns[iPanel]->SetSelectedItem(m_uiSelectedPanelInd+1); } CPUTButton* pButton = NULL; pGUI->CreateButton(_L("Fullscreen"), ID_FULLSCREEN_BUTTON, ID_MAIN_PANEL, &pButton); { CPUTCheckbox *pCheckBox = NULL; pGUI->CreateCheckbox( L"VSync", ID_ENABLE_VSYNC, ID_MAIN_PANEL, &pCheckBox); pCheckBox->SetCheckboxState( mSyncInterval ? CPUT_CHECKBOX_CHECKED : CPUT_CHECKBOX_UNCHECKED ); } { CPUTCheckbox *pCheckBox = NULL; pGUI->CreateCheckbox( L"Enable light scattering", ID_ENABLE_LIGHT_SCATTERING, ID_MAIN_PANEL, &pCheckBox); pCheckBox->SetCheckboxState( m_bEnableLightScattering ? CPUT_CHECKBOX_CHECKED : CPUT_CHECKBOX_UNCHECKED ); } { CPUTCheckbox *pCheckBox = NULL; pGUI->CreateCheckbox( L"Enable light shafts", ID_ENABLE_LIGHT_SHAFTS, ID_MAIN_PANEL, &pCheckBox); pCheckBox->SetCheckboxState( m_PPAttribs.m_bEnableLightShafts ? CPUT_CHECKBOX_CHECKED : CPUT_CHECKBOX_UNCHECKED ); } { CPUTDropdown* pDropDown = NULL; pGUI->CreateDropdown( L"Epipolar sampling", ID_LIGHT_SCTR_TECHNIQUE, ID_MAIN_PANEL, &pDropDown); pDropDown->AddSelectionItem( L"Brute force ray marching" ); // SelectedItem is 1-based pDropDown->SetSelectedItem(m_PPAttribs.m_uiLightSctrTechnique+1); } { CPUTDropdown* pDropDown = NULL; pGUI->CreateDropdown( L"Shadow Map res: 512x512", ID_SHADOW_MAP_RESOLUTION, ID_MAIN_PANEL, &pDropDown); pDropDown->AddSelectionItem( L"Shadow Map res: 1024x1024" ); pDropDown->AddSelectionItem( L"Shadow Map res: 2048x2048" ); pDropDown->AddSelectionItem( L"Shadow Map res: 4096x4096" ); unsigned long ulCurrVal; BitScanForward(&ulCurrVal, m_uiShadowMapResolution); // SelectedItem is 1-based pDropDown->SetSelectedItem(ulCurrVal-9 + 1); } { CPUTSlider* pSlider = NULL; std::wstringstream NumStepsSS; NumStepsSS << "Num integration steps: " << m_PPAttribs.m_uiInstrIntegralSteps; pGUI->CreateSlider( NumStepsSS.str().c_str(), ID_NUM_INTEGRATION_STEPS, ID_MAIN_PANEL, &pSlider); pSlider->SetEnable(!m_PPAttribs.m_bEnableLightShafts && m_PPAttribs.m_uiSingleScatteringMode == SINGLE_SCTR_MODE_INTEGRATION); unsigned long ulStartVal, ulEndVal; BitScanForward(&ulStartVal, m_iMinEpipolarSlices); BitScanForward(&ulEndVal, m_iMaxEpipolarSlices); pSlider->SetScale( 5, 100, 20 ); pSlider->SetValue( (float)m_PPAttribs.m_uiInstrIntegralSteps ); } { CPUTSlider* pSlider = NULL; std::wstringstream NumSlicesSS; NumSlicesSS << "Epipolar slices: " << m_PPAttribs.m_uiNumEpipolarSlices; pGUI->CreateSlider( NumSlicesSS.str().c_str(), ID_NUM_EPIPOLAR_SLICES, ID_MAIN_PANEL, &pSlider); unsigned long ulStartVal, ulEndVal, ulCurrVal; BitScanForward(&ulStartVal, m_iMinEpipolarSlices); BitScanForward(&ulEndVal, m_iMaxEpipolarSlices); pSlider->SetScale( (float)ulStartVal, (float)ulEndVal, ulEndVal-ulStartVal+1 ); BitScanForward(&ulCurrVal, m_PPAttribs.m_uiNumEpipolarSlices); pSlider->SetValue( (float)ulCurrVal ); } { CPUTSlider* pSlider = NULL; std::wstringstream NumSamplesSS; NumSamplesSS << "Total samples in slice: " << m_PPAttribs.m_uiMaxSamplesInSlice; pGUI->CreateSlider( NumSamplesSS.str().c_str(), ID_NUM_SAMPLES_IN_EPIPOLAR_SLICE, ID_MAIN_PANEL, &pSlider); unsigned long ulStartVal, ulEndVal, ulCurrVal; BitScanForward(&ulStartVal, m_iMinSamplesInEpipolarSlice); BitScanForward(&ulEndVal, m_iMaxSamplesInEpipolarSlice); pSlider->SetScale( (float)ulStartVal, (float)ulEndVal, ulEndVal-ulStartVal+1 ); BitScanForward(&ulCurrVal, m_PPAttribs.m_uiMaxSamplesInSlice); pSlider->SetValue( (float)ulCurrVal ); } { CPUTSlider* pSlider = NULL; std::wstringstream InitialSamplesStepSS; InitialSamplesStepSS << "Initial sample step: " << m_PPAttribs.m_uiInitialSampleStepInSlice; pGUI->CreateSlider( InitialSamplesStepSS.str().c_str(), ID_INITIAL_SAMPLE_STEP_IN_EPIPOLAR_SLICE, ID_MAIN_PANEL, &pSlider); unsigned long ulStartVal=0, ulEndVal, ulCurrVal; BitScanForward(&ulEndVal, m_PPAttribs.m_uiMaxSamplesInSlice / m_iMinInitialSamplesInEpipolarSlice); pSlider->SetScale( (float)ulStartVal, (float)ulEndVal, ulEndVal-ulStartVal+1 ); BitScanForward(&ulCurrVal, m_PPAttribs.m_uiInitialSampleStepInSlice); pSlider->SetValue( (float)ulCurrVal ); } { CPUTSlider* pSlider = NULL; std::wstringstream EpipoleSamplingDensitySS; EpipoleSamplingDensitySS << "Epipole sampling density: " << m_PPAttribs.m_uiEpipoleSamplingDensityFactor; pGUI->CreateSlider( EpipoleSamplingDensitySS.str().c_str(), ID_EPIPOLE_SAMPLING_DENSITY_FACTOR, ID_MAIN_PANEL, &pSlider); unsigned long ulStartVal=0, ulEndVal, ulCurrVal; BitScanForward(&ulEndVal, m_iMaxEpipoleSamplingDensityFactor); pSlider->SetScale( (float)ulStartVal, (float)ulEndVal, ulEndVal-ulStartVal+1 ); BitScanForward(&ulCurrVal, m_PPAttribs.m_uiEpipoleSamplingDensityFactor); pSlider->SetValue( (float)ulCurrVal ); } { CPUTSlider* pSlider = NULL; pGUI->CreateSlider( L"Refinement threshold", ID_REFINEMENT_THRESHOLD, ID_MAIN_PANEL, &pSlider); pSlider->SetScale( 0.001f, 0.5f, 100 ); pSlider->SetValue( m_PPAttribs.m_fRefinementThreshold ); } { CPUTSlider* pSlider = NULL; pGUI->CreateSlider( L"Scattering Scale", ID_SCATTERING_SCALE, ID_MAIN_PANEL, &pSlider); pSlider->SetScale( 0.1f, 2.f, 50 ); pSlider->SetValue( m_fScatteringScale ); } { CPUTCheckbox *pCheckBox = NULL; pGUI->CreateCheckbox( L"Show sampling", ID_SHOW_SAMPLING, ID_MAIN_PANEL, &pCheckBox); pCheckBox->SetCheckboxState( m_PPAttribs.m_bShowSampling ? CPUT_CHECKBOX_CHECKED : CPUT_CHECKBOX_UNCHECKED ); } { CPUTCheckbox *pCheckBox = NULL; pGUI->CreateCheckbox( L"1D Min/Max optimization", ID_MIN_MAX_SHADOW_MAP_OPTIMIZATION, ID_MAIN_PANEL, &pCheckBox); pCheckBox->SetCheckboxState( m_PPAttribs.m_bUse1DMinMaxTree ? CPUT_CHECKBOX_CHECKED : CPUT_CHECKBOX_UNCHECKED ); } { CPUTCheckbox *pCheckBox = NULL; pGUI->CreateCheckbox( L"Optimize sample locations", ID_OPTIMIZE_SAMPLE_LOCATIONS, ID_MAIN_PANEL, &pCheckBox); pCheckBox->SetCheckboxState( m_PPAttribs.m_bOptimizeSampleLocations ? CPUT_CHECKBOX_CHECKED : CPUT_CHECKBOX_UNCHECKED ); } { CPUTCheckbox *pCheckBox = NULL; pGUI->CreateCheckbox( L"Correction at depth breaks", ID_CORRECT_SCATTERING_AT_DEPTH_BREAKS, ID_MAIN_PANEL, &pCheckBox); pCheckBox->SetCheckboxState( m_PPAttribs.m_bCorrectScatteringAtDepthBreaks ? CPUT_CHECKBOX_CHECKED : CPUT_CHECKBOX_UNCHECKED ); } { CPUTCheckbox *pCheckBox = NULL; pGUI->CreateCheckbox( L"Show depth breaks", ID_SHOW_DEPTH_BREAKS, ID_MAIN_PANEL, &pCheckBox); pCheckBox->SetCheckboxState( m_PPAttribs.m_bShowDepthBreaks ? CPUT_CHECKBOX_CHECKED : CPUT_CHECKBOX_UNCHECKED ); } { CPUTCheckbox *pCheckBox = NULL; pGUI->CreateCheckbox( L"Lighting only", ID_SHOW_LIGHTING_ONLY_CHECK, ID_MAIN_PANEL, &pCheckBox); pCheckBox->SetCheckboxState( m_PPAttribs.m_bShowLightingOnly ? CPUT_CHECKBOX_CHECKED : CPUT_CHECKBOX_UNCHECKED ); } { CPUTDropdown *pDropDown = NULL; pGUI->CreateDropdown( L"Sngl sctr: none", ID_SINGLE_SCTR_MODE_DROPDOWN, ID_ADDITIONAL_ATTRIBS_PANEL, &pDropDown); pDropDown->AddSelectionItem( L"Sngl sctr: integration" ); pDropDown->AddSelectionItem( L"Sngl sctr: LUT" ); pDropDown->SetSelectedItem( m_PPAttribs.m_uiSingleScatteringMode+1 ); } { CPUTDropdown *pDropDown = NULL; pGUI->CreateDropdown( L"Mult sctr: none", ID_MULTIPLE_SCTR_MODE_DROPDOWN, ID_ADDITIONAL_ATTRIBS_PANEL, &pDropDown); pDropDown->AddSelectionItem( L"Mult sctr: unoccluded" ); pDropDown->AddSelectionItem( L"Mult sctr: occluded" ); pDropDown->SetSelectedItem( m_PPAttribs.m_uiMultipleScatteringMode+1 ); } { CPUTDropdown *pDropDown = NULL; pGUI->CreateDropdown( L"Num Cascades: 1", ID_NUM_CASCADES_DROPDOWN, ID_ADDITIONAL_ATTRIBS_PANEL, &pDropDown); for(int i=2; i <= MAX_CASCADES; ++i) { WCHAR Text[32]; _stprintf_s(Text, _countof(Text), L"Num Cascades: %d", i); pDropDown->AddSelectionItem( Text ); } pDropDown->SetSelectedItem( m_TerrainRenderParams.m_iNumShadowCascades ); } { CPUTCheckbox *pCheckBox = NULL; pGUI->CreateCheckbox( L"Show cascades", ID_SHOW_CASCADES_CHECK, ID_ADDITIONAL_ATTRIBS_PANEL, &pCheckBox); pCheckBox->SetCheckboxState( CPUT_CHECKBOX_UNCHECKED ); } { CPUTCheckbox *pCheckBox = NULL; pGUI->CreateCheckbox( L"Smooth shadows", ID_SMOOTH_SHADOWS_CHECK, ID_ADDITIONAL_ATTRIBS_PANEL, &pCheckBox); pCheckBox->SetCheckboxState( m_TerrainRenderParams.m_bSmoothShadows ? CPUT_CHECKBOX_CHECKED : CPUT_CHECKBOX_UNCHECKED ); } { CPUTCheckbox *pCheckBox = NULL; pGUI->CreateCheckbox( L"Best cascade search", ID_BEST_CASCADE_SEARCH_CHECK, ID_ADDITIONAL_ATTRIBS_PANEL, &pCheckBox); pCheckBox->SetCheckboxState( m_TerrainRenderParams.m_bBestCascadeSearch ? CPUT_CHECKBOX_CHECKED : CPUT_CHECKBOX_UNCHECKED ); } { CPUTSlider *pSlider = NULL; std::wstringstream SS; SS << "Partitioning Factor: " << m_fCascadePartitioningFactor; pGUI->CreateSlider( SS.str().c_str(), ID_CASCADE_PARTITIONING_SLIDER, ID_ADDITIONAL_ATTRIBS_PANEL, &pSlider); pSlider->SetScale( 0.0f, 1.f, 101 ); pSlider->SetValue( m_fCascadePartitioningFactor ); } { CPUTDropdown *pDropDown = NULL; pGUI->CreateDropdown( L"Cascades processing: single pass", ID_CASCADE_PROCESSING_MODE_DROPDOWN, ID_ADDITIONAL_ATTRIBS_PANEL, &pDropDown); pDropDown->AddSelectionItem( L"Cascades processing: multi pass" ); pDropDown->AddSelectionItem( L"Cascades processing: multi pass inst" ); pDropDown->SetSelectedItem( m_PPAttribs.m_uiCascadeProcessingMode+1 ); } { CPUTDropdown *pDropDown = NULL; pGUI->CreateDropdown( L"First cascade to ray march: 0", ID_FIRST_CASCADE_TO_RAY_MARCH_DROPDOWN, ID_ADDITIONAL_ATTRIBS_PANEL, &pDropDown); for(int i=1; iAddSelectionItem( Text ); } pDropDown->SetSelectedItem( m_PPAttribs.m_iFirstCascade+1 ); } { CPUTDropdown *pDropDown = NULL; pGUI->CreateDropdown( L"Extinction eval mode: per pixel", ID_EXTINCTION_EVAL_MODE_DROPDOWN, ID_ADDITIONAL_ATTRIBS_PANEL, &pDropDown); pDropDown->AddSelectionItem( L"Extinction eval mode: Epipolar" ); pDropDown->SetSelectedItem( m_PPAttribs.m_uiExtinctionEvalMode+1 ); } { CPUTDropdown *pDropDown = NULL; pGUI->CreateDropdown( L"Refinement criterion: depth", ID_REFINEMENT_CRITERION_DROPDOWN, ID_ADDITIONAL_ATTRIBS_PANEL, &pDropDown); pDropDown->AddSelectionItem( L"Refinement criterion: inscattering" ); pDropDown->SetSelectedItem( m_PPAttribs.m_uiRefinementCriterion+1 ); } { CPUTDropdown *pDropDown = NULL; pGUI->CreateDropdown( L"Min/max format: 16u", ID_MIN_MAX_MIP_FORMAT_DROPDOWN, ID_ADDITIONAL_ATTRIBS_PANEL, &pDropDown); pDropDown->AddSelectionItem( L"Min/max format: 32f" ); pDropDown->SetSelectedItem( m_PPAttribs.m_bIs32BitMinMaxMipMap ? 2 : 1 ); } { CPUTCheckbox *pCheckBox = NULL; pGUI->CreateCheckbox( L"Use custom sctr coeffs", ID_USE_CUSTOM_SCTR_COEFFS_CHECK, ID_ADDITIONAL_ATTRIBS_PANEL, &pCheckBox); pCheckBox->SetCheckboxState( m_PPAttribs.m_bUseCustomSctrCoeffs ? CPUT_CHECKBOX_CHECKED : CPUT_CHECKBOX_UNCHECKED ); } { CPUTButton *pBtn = NULL; pGUI->CreateButton( L"Set Rayleigh color", ID_RLGH_COLOR_BTN, ID_ADDITIONAL_ATTRIBS_PANEL, &pBtn); pBtn->SetEnable(!CPUTGetFullscreenState() && m_PPAttribs.m_bUseCustomSctrCoeffs); } { CPUTButton *pBtn = NULL; pGUI->CreateButton( L"Set Mie color", ID_MIE_COLOR_BTN, ID_ADDITIONAL_ATTRIBS_PANEL, &pBtn); pBtn->SetEnable(!CPUTGetFullscreenState() && m_PPAttribs.m_bUseCustomSctrCoeffs); } { CPUTSlider* pSlider = NULL; pGUI->CreateSlider( L"Aerosol density", ID_AEROSOL_DENSITY_SCALE_SLIDER, ID_ADDITIONAL_ATTRIBS_PANEL, &pSlider); pSlider->SetScale( 0.1f, 5.f, 50 ); pSlider->SetValue( m_PPAttribs.m_fAerosolDensityScale ); } { CPUTSlider* pSlider = NULL; pGUI->CreateSlider( L"Aerosol absorbtion", ID_AEROSOL_ABSORBTION_SCALE_SLIDER, ID_ADDITIONAL_ATTRIBS_PANEL, &pSlider); pSlider->SetScale( 0.0f, 5.f, 50 ); pSlider->SetValue( m_PPAttribs.m_fAerosolAbsorbtionScale ); } { CPUTCheckbox *pCheckBox = NULL; pGUI->CreateCheckbox( L"Animate sun", ID_ANIMATE_SUN, ID_ADDITIONAL_ATTRIBS_PANEL, &pCheckBox); pCheckBox->SetCheckboxState( m_bAnimateSun ? CPUT_CHECKBOX_CHECKED : CPUT_CHECKBOX_UNCHECKED ); } { CPUTSlider* pSlider = NULL; pGUI->CreateSlider( L"Middle gray (Key)", ID_MIDDLE_GRAY, ID_TONE_MAPPING_ATTRIBS_PANEL, &pSlider); pSlider->SetScale( 0.01f, 1.f, 50 ); pSlider->SetValue( m_PPAttribs.m_fMiddleGray ); } { CPUTSlider* pSlider = NULL; pGUI->CreateSlider( L"White point", ID_WHITE_POINT, ID_TONE_MAPPING_ATTRIBS_PANEL, &pSlider); pSlider->SetScale( 0.01f, 10.f, 50 ); pSlider->SetValue( m_PPAttribs.m_fWhitePoint ); pSlider->SetEnable( m_PPAttribs.m_uiToneMappingMode == TONE_MAPPING_MODE_REINHARD_MOD || m_PPAttribs.m_uiToneMappingMode == TONE_MAPPING_MODE_UNCHARTED2 || m_PPAttribs.m_uiToneMappingMode == TONE_MAPPING_LOGARITHMIC || m_PPAttribs.m_uiToneMappingMode == TONE_MAPPING_ADAPTIVE_LOG ); } { CPUTSlider* pSlider = NULL; pGUI->CreateSlider( L"Luminance saturation", ID_LUM_SATURATION, ID_TONE_MAPPING_ATTRIBS_PANEL, &pSlider); pSlider->SetScale( 0.01f, 2.f, 50 ); pSlider->SetValue( m_PPAttribs.m_fLuminanceSaturation ); pSlider->SetEnable( m_PPAttribs.m_uiToneMappingMode == TONE_MAPPING_MODE_EXP || m_PPAttribs.m_uiToneMappingMode == TONE_MAPPING_MODE_REINHARD || m_PPAttribs.m_uiToneMappingMode == TONE_MAPPING_MODE_REINHARD_MOD || m_PPAttribs.m_uiToneMappingMode == TONE_MAPPING_LOGARITHMIC || m_PPAttribs.m_uiToneMappingMode == TONE_MAPPING_ADAPTIVE_LOG ); } { CPUTCheckbox *pCheckBox = NULL; pGUI->CreateCheckbox( L"Auto exposure", ID_AUTO_EXPOSURE, ID_TONE_MAPPING_ATTRIBS_PANEL, &pCheckBox); pCheckBox->SetCheckboxState( m_PPAttribs.m_bAutoExposure ? CPUT_CHECKBOX_CHECKED : CPUT_CHECKBOX_UNCHECKED ); } { CPUTDropdown *pDropDown = NULL; pGUI->CreateDropdown( L"Tone mapping: exp", ID_TONE_MAPPING_MODE, ID_TONE_MAPPING_ATTRIBS_PANEL, &pDropDown); pDropDown->AddSelectionItem( L"Tone mapping: Reinhard" ); pDropDown->AddSelectionItem( L"Tone mapping: Reinhard Mod" ); pDropDown->AddSelectionItem( L"Tone mapping: Uncharted 2" ); pDropDown->AddSelectionItem( L"Tone mapping: Filmic ALU" ); pDropDown->AddSelectionItem( L"Tone mapping: Logarithmic" ); pDropDown->AddSelectionItem( L"Tone mapping: Adaptive log" ); pDropDown->SetSelectedItem( m_PPAttribs.m_uiToneMappingMode + 1 ); } { CPUTCheckbox *pCheckBox = NULL; pGUI->CreateCheckbox( L"Light adaptation", ID_LIGHT_ADAPTATION, ID_TONE_MAPPING_ATTRIBS_PANEL, &pCheckBox); pCheckBox->SetCheckboxState( m_PPAttribs.m_bLightAdaptation ? CPUT_CHECKBOX_CHECKED : CPUT_CHECKBOX_UNCHECKED ); pCheckBox->SetEnable(m_PPAttribs.m_bAutoExposure ? true : false ); } pGUI->CreateText( _L("F1 for Help"), ID_IGNORE_CONTROL_ID, ID_HELP_TEXT_PANEL); pGUI->CreateText( _L("[Escape] to quit application"), ID_IGNORE_CONTROL_ID, ID_HELP_TEXT_PANEL); pGUI->CreateText( _L("A,S,D,F - move camera position"), ID_IGNORE_CONTROL_ID, ID_HELP_TEXT_PANEL); pGUI->CreateText( _L("Q - camera position down"), ID_IGNORE_CONTROL_ID, ID_HELP_TEXT_PANEL); pGUI->CreateText( _L("E - camera position up"), ID_IGNORE_CONTROL_ID, ID_HELP_TEXT_PANEL); pGUI->CreateText( _L("[Shift] - accelerate camera movement"), ID_IGNORE_CONTROL_ID, ID_HELP_TEXT_PANEL); pGUI->CreateText( _L("mouse + left click - camera look rotation"), ID_IGNORE_CONTROL_ID, ID_HELP_TEXT_PANEL); pGUI->CreateText( _L("mouse + right click - light rotation"), ID_IGNORE_CONTROL_ID, ID_HELP_TEXT_PANEL); // // Make the main panel active // pGUI->SetActivePanel(CONTROL_PANEL_IDS[m_uiSelectedPanelInd]); CreateTmpBackBuffAndDepthBuff(mpD3dDevice); // Create shadow map before other assets!!! HRESULT hResult = CreateShadowMap(mpD3dDevice); if( FAILED( hResult ) ) return; pAssetLibrary->SetMediaDirectoryName( _L("Media\\")); // Add our programatic (and global) material parameters CPUTMaterial::mGlobalProperties.AddValue( _L("cbPerFrameValues"), _L("$cbPerFrameValues") ); CPUTMaterial::mGlobalProperties.AddValue( _L("cbPerModelValues"), _L("#cbPerModelValues") ); int width, height; CPUTOSServices::GetOSServices()->GetClientDimensions(&width, &height); CPUTRenderStateBlockDX11 *pBlock = new CPUTRenderStateBlockDX11(); CPUTRenderStateDX11 *pStates = pBlock->GetState(); // Override default sampler desc for our default shadowing sampler pStates->SamplerDesc[1].Filter = D3D11_FILTER_COMPARISON_MIN_MAG_LINEAR_MIP_POINT; pStates->SamplerDesc[1].AddressU = D3D11_TEXTURE_ADDRESS_BORDER; pStates->SamplerDesc[1].AddressV = D3D11_TEXTURE_ADDRESS_BORDER; pStates->SamplerDesc[1].ComparisonFunc = D3D11_COMPARISON_GREATER; pBlock->CreateNativeResources(); CPUTAssetLibrary::GetAssetLibrary()->AddRenderStateBlock( _L("$DefaultRenderStates"), pBlock ); pBlock->Release(); // We're done with it. The library owns it now. // Initialize mpCamera = new CPUTCamera(); CPUTAssetLibraryDX11::GetAssetLibrary()->AddCamera( _L("Outdoor light scattering sample camera"), mpCamera ); // Set the projection matrix for all of the cameras to match our window. mpCamera->SetAspectRatio(((float)width)/((float)height)); mpCamera->SetFov( XMConvertToRadians(45.0f) ); mpCamera->SetFarPlaneDistance(1e+7); mpCamera->SetNearPlaneDistance(50.0f); float4x4 InitialWorldMatrix ( -0.88250709f, 0.00000000f, 0.47029909f, 0.0000000f, -0.10293237f, 0.97575504f, -0.19315059f, 0.0000000f, -0.45889670f, -0.21886577f, -0.86111075f, 0.0000000f, 0.f, 8023.6152f, 0.f, 1.0000000f ); mpCamera->SetParentMatrix(InitialWorldMatrix); mpCamera->Update(); mpCameraController = new CPUTCameraControllerFPS(); mpCameraController->SetCamera(mpCamera); mpCameraController->SetLookSpeed(0.004f); mpCameraController->SetMoveSpeed(200.0f); // // Create camera // m_pDirectionalLightCamera = new CParallelLightCamera(); m_pDirLightOrienationCamera = new CParallelLightCamera(); float4x4 LightOrientationWorld ( -0.92137718f, -0.36748588f, -0.12656364f, 0.0000000f, -0.37707147f, 0.92411846f, 0.061823435f, 0.0000000f, 0.094240554f, 0.10468624f, -0.99003011f, 0.0000000f, 0.f, 0.f, 0.f, 1.0000000f ); m_pDirLightOrienationCamera->SetParentMatrix( LightOrientationWorld ); m_pDirLightOrienationCamera->Update(); m_pLightController = new CPUTCameraControllerArcBall(); m_pLightController->SetCamera( m_pDirLightOrienationCamera ); m_pLightController->SetLookSpeed(0.002f); // Call ResizeWindow() because it creates some resources that our blur material needs (e.g., the back buffer) ResizeWindow(width, height); /* * Create DX resources */ // Initialize the post process object to the device and context hResult = m_pLightSctrPP->OnCreateDevice(mpD3dDevice, mpContext); if( FAILED( hResult ) ) return; // Create data source try { m_pElevDataSource.reset( new CElevationDataSource(m_strRawDEMDataFile.c_str()) ); m_pElevDataSource->SetOffsets(m_TerrainRenderParams.m_iColOffset, m_TerrainRenderParams.m_iRowOffset); m_fMinElevation = m_pElevDataSource->GetGlobalMinElevation() * m_TerrainRenderParams.m_TerrainAttribs.m_fElevationScale; m_fMaxElevation = m_pElevDataSource->GetGlobalMaxElevation() * m_TerrainRenderParams.m_TerrainAttribs.m_fElevationScale; } catch(const std::exception &) { LOG_ERROR(_T("Failed to create elevation data source")); return; } LPCTSTR strTileTexPaths[CEarthHemsiphere::NUM_TILE_TEXTURES], strNormalMapPaths[CEarthHemsiphere::NUM_TILE_TEXTURES]; for(int iTile=0; iTile < _countof(strTileTexPaths); ++iTile ) { strTileTexPaths[iTile] = m_strTileTexPaths[iTile].c_str(); strNormalMapPaths[iTile] = m_strNormalMapTexPaths[iTile].c_str(); } V( m_EarthHemisphere.OnD3D11CreateDevice(m_pElevDataSource.get(), m_TerrainRenderParams, mpD3dDevice, mpContext, m_strRawDEMDataFile.c_str(), m_strMtrlMaskFile.c_str(), strTileTexPaths, strNormalMapPaths ) ); D3D11_BUFFER_DESC CBDesc = { sizeof(SLightAttribs), D3D11_USAGE_DYNAMIC, D3D11_BIND_CONSTANT_BUFFER, D3D11_CPU_ACCESS_WRITE, //UINT CPUAccessFlags 0, //UINT MiscFlags; 0, //UINT StructureByteStride; }; V( mpD3dDevice->CreateBuffer( &CBDesc, NULL, &m_pcbLightAttribs) ); } void GetRaySphereIntersection(D3DXVECTOR3 f3RayOrigin, const D3DXVECTOR3 &f3RayDirection, const D3DXVECTOR3 &f3SphereCenter, float fSphereRadius, D3DXVECTOR2 &f2Intersections) { // http://wiki.cgsociety.org/index.php/Ray_Sphere_Intersection f3RayOrigin -= f3SphereCenter; float A = D3DXVec3Dot(&f3RayDirection, &f3RayDirection); float B = 2 * D3DXVec3Dot(&f3RayOrigin, &f3RayDirection); float C = D3DXVec3Dot(&f3RayOrigin, &f3RayOrigin) - fSphereRadius*fSphereRadius; float D = B*B - 4*A*C; // If discriminant is negative, there are no real roots hence the ray misses the // sphere if( D<0 ) { f2Intersections = D3DXVECTOR2(-1,-1); } else { D = sqrt(D); f2Intersections = D3DXVECTOR2(-B - D, -B + D) / (2*A); // A must be positive here!! } } extern float3 gLightDir; void COutdoorLightScatteringSample::RenderShadowMap(ID3D11DeviceContext *pContext, SLightAttribs &LightAttribs) { //CPUTRenderParametersDX drawParams(pContext); SShadowMapAttribs& ShadowMapAttribs = LightAttribs.ShadowAttribs; //CPUTCamera *pLastCamera = mpCamera; D3DXVECTOR3 v3DirOnLight = (D3DXVECTOR3&)LightAttribs.f4DirOnLight; D3DXVECTOR3 v3LightDirection = -v3DirOnLight; gLightDir.x = v3LightDirection.x; gLightDir.y = v3LightDirection.y; gLightDir.z = v3LightDirection.z; // Declare working vectors D3DXVECTOR3 vLightSpaceX, vLightSpaceY, vLightSpaceZ; // Compute an inverse vector for the direction on the sun vLightSpaceZ = v3LightDirection; // And a vector for X light space vLightSpaceX = D3DXVECTOR3( 1.0f, 0.0, 0.0 ); // Compute the cross products D3DXVec3Cross(&vLightSpaceY, &vLightSpaceX, &vLightSpaceZ); D3DXVec3Cross(&vLightSpaceX, &vLightSpaceZ, &vLightSpaceY); // And then normalize them D3DXVec3Normalize( &vLightSpaceX, &vLightSpaceX ); D3DXVec3Normalize( &vLightSpaceY, &vLightSpaceY ); D3DXVec3Normalize( &vLightSpaceZ, &vLightSpaceZ ); // Declare a world to light space transformation matrix // Initialize to an identity matrix D3DXMATRIX WorldToLightViewSpaceMatr; D3DXMatrixIdentity( &WorldToLightViewSpaceMatr ); // Adjust elements to the light space WorldToLightViewSpaceMatr._11 = vLightSpaceX.x; WorldToLightViewSpaceMatr._21 = vLightSpaceX.y; WorldToLightViewSpaceMatr._31 = vLightSpaceX.z; WorldToLightViewSpaceMatr._12 = vLightSpaceY.x; WorldToLightViewSpaceMatr._22 = vLightSpaceY.y; WorldToLightViewSpaceMatr._32 = vLightSpaceY.z; WorldToLightViewSpaceMatr._13 = vLightSpaceZ.x; WorldToLightViewSpaceMatr._23 = vLightSpaceZ.y; WorldToLightViewSpaceMatr._33 = vLightSpaceZ.z; D3DXMatrixTranspose(&ShadowMapAttribs.mWorldToLightViewT, &WorldToLightViewSpaceMatr); D3DXVECTOR3 f3CameraPosInLightSpace; D3DXVec3TransformCoord(&f3CameraPosInLightSpace, &m_CameraPos, &WorldToLightViewSpaceMatr); D3DXMATRIX mProj = (D3DXMATRIX &)*mpCamera->GetProjectionMatrix(); float fMainCamNearPlane = -mProj._43 / mProj._33; float fMainCamFarPlane = mProj._33 / (mProj._33-1) * fMainCamNearPlane; //fMainCamNearPlane = min(fMainCamNearPlane, 2e+5f); for(int i=0; i < MAX_CASCADES; ++i) ShadowMapAttribs.fCascadeCamSpaceZEnd[i] = +FLT_MAX; CComPtr pOrigRTV; CComPtr pOrigDSV; D3D11_VIEWPORT OrigViewport; pContext->OMGetRenderTargets(1, &pOrigRTV, &pOrigDSV); UINT uiNumVP = 1; pContext->RSGetViewports(&uiNumVP, &OrigViewport); // Render cascades for(int iCascade = 0; iCascade < m_TerrainRenderParams.m_iNumShadowCascades; ++iCascade) { auto &CurrCascade = ShadowMapAttribs.Cascades[iCascade]; D3DXMATRIX CascadeFrustumProjMatrix; float &fCascadeNearZ = ShadowMapAttribs.fCascadeCamSpaceZEnd[iCascade]; float fCascadeFarZ = (iCascade == 0) ? fMainCamFarPlane : ShadowMapAttribs.fCascadeCamSpaceZEnd[iCascade-1]; fCascadeNearZ = fMainCamNearPlane; if (iCascade < m_TerrainRenderParams.m_iNumShadowCascades-1) { float ratio = fMainCamNearPlane / fMainCamFarPlane; float power = (float)(iCascade+1) / (float)m_TerrainRenderParams.m_iNumShadowCascades; float logZ = fMainCamFarPlane * pow(ratio, power); float range = fMainCamNearPlane - fMainCamFarPlane; float uniformZ = fMainCamFarPlane + range * power; fCascadeNearZ = m_fCascadePartitioningFactor * (logZ - uniformZ) + uniformZ; } float fMaxLightShaftsDist = 3e+5f; CurrCascade.f4StartEndZ.x = (iCascade == m_PPAttribs.m_iFirstCascade) ? 0 : min(fCascadeFarZ, fMaxLightShaftsDist); CurrCascade.f4StartEndZ.y = min(fCascadeNearZ, fMaxLightShaftsDist); CascadeFrustumProjMatrix = mProj; CascadeFrustumProjMatrix._33 = fCascadeFarZ / (fCascadeFarZ - fCascadeNearZ); CascadeFrustumProjMatrix._43 = -fCascadeNearZ * CascadeFrustumProjMatrix._33; D3DXMATRIX CascadeFrustumViewProjMatr = m_CameraViewMatrix * CascadeFrustumProjMatrix; D3DXMATRIX CascadeFrustumProjSpaceToWorldSpace; D3DXMatrixInverse(&CascadeFrustumProjSpaceToWorldSpace, nullptr, &CascadeFrustumViewProjMatr); D3DXMATRIX CascadeFrustumProjSpaceToLightSpace = CascadeFrustumProjSpaceToWorldSpace * WorldToLightViewSpaceMatr; // Set reference minimums and maximums for each coordinate D3DXVECTOR3 f3MinXYZ(f3CameraPosInLightSpace), f3MaxXYZ(f3CameraPosInLightSpace); // First cascade used for ray marching must contain camera within it if( iCascade != m_PPAttribs.m_iFirstCascade ) { f3MinXYZ = D3DXVECTOR3(+FLT_MAX, +FLT_MAX, +FLT_MAX); f3MaxXYZ = D3DXVECTOR3(-FLT_MAX, -FLT_MAX, -FLT_MAX); } for(int iClipPlaneCorner=0; iClipPlaneCorner < 8; ++iClipPlaneCorner) { D3DXVECTOR3 f3PlaneCornerProjSpace( (iClipPlaneCorner & 0x01) ? +1.f : - 1.f, (iClipPlaneCorner & 0x02) ? +1.f : - 1.f, // Since we use complimentary depth buffering, // far plane has depth 0 (iClipPlaneCorner & 0x04) ? 1.f : 0.f); D3DXVECTOR3 f3PlaneCornerLightSpace; D3DXVec3TransformCoord(&f3PlaneCornerLightSpace, &f3PlaneCornerProjSpace, &CascadeFrustumProjSpaceToLightSpace); D3DXVec3Minimize(&f3MinXYZ, &f3MinXYZ, &f3PlaneCornerLightSpace); D3DXVec3Maximize(&f3MaxXYZ, &f3MaxXYZ, &f3PlaneCornerLightSpace); } // It is necessary to ensure that shadow-casting patches, which are not visible // in the frustum, are still rendered into the shadow map f3MinXYZ.z -= SAirScatteringAttribs().fEarthRadius * sqrt(2.f); // Align cascade extent to the closest power of two float fShadowMapDim = (float)m_uiShadowMapResolution; float fCascadeXExt = (f3MaxXYZ.x - f3MinXYZ.x) * (1 + 1.f/fShadowMapDim); float fCascadeYExt = (f3MaxXYZ.y - f3MinXYZ.y) * (1 + 1.f/fShadowMapDim); const float fExtStep = 2.f; fCascadeXExt = pow( fExtStep, ceil( log(fCascadeXExt)/log(fExtStep) ) ); fCascadeYExt = pow( fExtStep, ceil( log(fCascadeYExt)/log(fExtStep) ) ); // Align cascade center with the shadow map texels to alleviate temporal aliasing float fCascadeXCenter = (f3MaxXYZ.x + f3MinXYZ.x)/2.f; float fCascadeYCenter = (f3MaxXYZ.y + f3MinXYZ.y)/2.f; float fTexelXSize = fCascadeXExt / fShadowMapDim; float fTexelYSize = fCascadeXExt / fShadowMapDim; fCascadeXCenter = floor(fCascadeXCenter/fTexelXSize) * fTexelXSize; fCascadeYCenter = floor(fCascadeYCenter/fTexelYSize) * fTexelYSize; // Compute new cascade min/max xy coords f3MaxXYZ.x = fCascadeXCenter + fCascadeXExt/2.f; f3MinXYZ.x = fCascadeXCenter - fCascadeXExt/2.f; f3MaxXYZ.y = fCascadeYCenter + fCascadeYExt/2.f; f3MinXYZ.y = fCascadeYCenter - fCascadeYExt/2.f; CurrCascade.f4LightSpaceScale.x = 2.f / (f3MaxXYZ.x - f3MinXYZ.x); CurrCascade.f4LightSpaceScale.y = 2.f / (f3MaxXYZ.y - f3MinXYZ.y); CurrCascade.f4LightSpaceScale.z = -1.f / (f3MaxXYZ.z - f3MinXYZ.z); // Apply bias to shift the extent to [-1,1]x[-1,1]x[1,0] CurrCascade.f4LightSpaceScaledBias.x = -f3MinXYZ.x * CurrCascade.f4LightSpaceScale.x - 1.f; CurrCascade.f4LightSpaceScaledBias.y = -f3MinXYZ.y * CurrCascade.f4LightSpaceScale.y - 1.f; CurrCascade.f4LightSpaceScaledBias.z = -f3MaxXYZ.z * CurrCascade.f4LightSpaceScale.z + 0.f; D3DXMATRIX ScaleMatrix; D3DXMatrixScaling(&ScaleMatrix, CurrCascade.f4LightSpaceScale.x, CurrCascade.f4LightSpaceScale.y, CurrCascade.f4LightSpaceScale.z); D3DXMATRIX ScaledBiasMatrix; D3DXMatrixTranslation(&ScaledBiasMatrix, CurrCascade.f4LightSpaceScaledBias.x, CurrCascade.f4LightSpaceScaledBias.y, CurrCascade.f4LightSpaceScaledBias.z); // Note: bias is applied after scaling! D3DXMATRIX CascadeProjMatr = ScaleMatrix * ScaledBiasMatrix; //D3DXMatrixOrthoOffCenterLH( &m_LightOrthoMatrix, MinX, MaxX, MinY, MaxY, MaxZ, MinZ); // Adjust the world to light space transformation matrix D3DXMATRIX WorldToLightProjSpaceMatr = WorldToLightViewSpaceMatr * CascadeProjMatr; D3DXMATRIX ProjToUVScale, ProjToUVBias; D3DXMatrixScaling( &ProjToUVScale, 0.5f, -0.5f, 1.f); D3DXMatrixTranslation( &ProjToUVBias, 0.5f, 0.5f, 0.f); D3DXMATRIX WorldToShadowMapUVDepthMatr = WorldToLightProjSpaceMatr * ProjToUVScale * ProjToUVBias; D3DXMatrixTranspose( &ShadowMapAttribs.mWorldToShadowMapUVDepthT[iCascade], &WorldToShadowMapUVDepthMatr ); D3D11_VIEWPORT NewViewPort; NewViewPort.TopLeftX = 0; NewViewPort.TopLeftY = 0; NewViewPort.Width = static_cast( m_uiShadowMapResolution ); NewViewPort.Height = static_cast( m_uiShadowMapResolution ); NewViewPort.MinDepth = 0; NewViewPort.MaxDepth = 1; // Set the viewport pContext->RSSetViewports(1, &NewViewPort); pContext->OMSetRenderTargets(0, nullptr, m_pShadowMapDSVs[iCascade]); pContext->ClearDepthStencilView(m_pShadowMapDSVs[iCascade], D3D11_CLEAR_DEPTH, 0.f, 0); // Render terrain to shadow map m_EarthHemisphere.Render(mpContext, m_CameraPos, WorldToLightProjSpaceMatr, nullptr, nullptr, nullptr, nullptr, nullptr, true); } pContext->OMSetRenderTargets(1, &pOrigRTV.p, pOrigDSV); pContext->RSSetViewports(1, &OrigViewport); } void ComputeApproximateNearFarPlaneDist(const D3DXVECTOR3 &CameraPos, const D3DXMATRIX &ViewMatr, const D3DXMATRIX &ProjMatr, const D3DXVECTOR3 &EarthCenter, float fEarthRadius, float fMinRadius, float fMaxRadius, float &fNearPlaneZ, float &fFarPlaneZ) { D3DXMATRIX ViewProjMatr = ViewMatr * ProjMatr; D3DXMATRIX ViewProjInv; D3DXMatrixInverse(&ViewProjInv, nullptr, &ViewProjMatr); // Compute maximum view distance for the current camera altitude D3DXVECTOR3 f3CameraGlobalPos = CameraPos - EarthCenter; float fCameraElevationSqr = D3DXVec3Dot(&f3CameraGlobalPos, &f3CameraGlobalPos); float fMaxViewDistance = (float)(sqrt( (double)fCameraElevationSqr - (double)fEarthRadius*fEarthRadius ) + sqrt( (double)fMaxRadius*fMaxRadius - (double)fEarthRadius*fEarthRadius )); float fCameraElev = sqrt(fCameraElevationSqr); fNearPlaneZ = 50.f; if( fCameraElev > fMaxRadius ) { // Adjust near clipping plane fNearPlaneZ = (fCameraElev - fMaxRadius) / sqrt( 1 + 1.f/(ProjMatr._11*ProjMatr._11) + 1.f/(ProjMatr._22*ProjMatr._22) ); } fNearPlaneZ = max(fNearPlaneZ, 50); fFarPlaneZ = 1000; const int iNumTestDirections = 5; for(int i=0; i 0 ? IsecsWithBottomBoundSphere.x : IsecsWithBottomBoundSphere.y; if( fNearIsecWithBottomSphere > 0 ) { // The ray hits the Earth. Use hit point to compute camera space Z D3DXVECTOR3 HitPointWS = CameraPos + DirFromCamera*fNearIsecWithBottomSphere; D3DXVECTOR3 HitPointCamSpace; D3DXVec3TransformCoord(&HitPointCamSpace, &HitPointWS, &ViewMatr); fFarPlaneZ = max(fFarPlaneZ, HitPointCamSpace.z); } else { // The ray misses the Earth. In that case the whole earth could be seen fFarPlaneZ = fMaxViewDistance; } } } //----------------------------------------------------------------------------- void COutdoorLightScatteringSample::Update(double deltaSeconds) { if( m_bAnimateSun ) { auto &LightOrientationMatrix = *m_pDirLightOrienationCamera->GetParentMatrix(); float3 RotationAxis( 0.5f, 0.3f, 0.0f ); float3 LightDir = m_pDirLightOrienationCamera->GetLook() * -1; float fRotationScaler = ( LightDir.y > +0.2f ) ? 50.f : 1.f; float4x4 RotationMatrix = float4x4RotationAxis(RotationAxis, 0.02f * (float)deltaSeconds * fRotationScaler); LightOrientationMatrix = LightOrientationMatrix * RotationMatrix; m_pDirLightOrienationCamera->SetParentMatrix(LightOrientationMatrix); } if( mpCameraController ) { float fSpeedScale = max( (m_CameraPos.y-5000)/20, 200.f ); mpCameraController->SetMoveSpeed(fSpeedScale); mpCameraController->Update( static_cast(deltaSeconds) ); } mpCamera->GetPosition(&m_CameraPos.x, &m_CameraPos.y, &m_CameraPos.z); float fTerrainHeightUnderCamera = m_pElevDataSource->GetInterpolatedHeight(m_CameraPos.x/m_TerrainRenderParams.m_TerrainAttribs.m_fElevationSamplingInterval, m_CameraPos.z/m_TerrainRenderParams.m_TerrainAttribs.m_fElevationSamplingInterval) * m_TerrainRenderParams.m_TerrainAttribs.m_fElevationScale; fTerrainHeightUnderCamera += 100.f; float fXZMoveRadius = 512 * m_TerrainRenderParams.m_TerrainAttribs.m_fElevationSamplingInterval; bool bUpdateCamPos = false; float fDistFromCenter = sqrt(m_CameraPos.x*m_CameraPos.x + m_CameraPos.z*m_CameraPos.z); if( fDistFromCenter > fXZMoveRadius ) { m_CameraPos.x *= fXZMoveRadius/fDistFromCenter; m_CameraPos.z *= fXZMoveRadius/fDistFromCenter; bUpdateCamPos = true; } if( m_CameraPos.y < fTerrainHeightUnderCamera ) { m_CameraPos.y = fTerrainHeightUnderCamera; bUpdateCamPos = true; } float fMaxCameraAltitude = SAirScatteringAttribs().fAtmTopHeight * 10; if( m_CameraPos.y > fMaxCameraAltitude ) { m_CameraPos.y = fMaxCameraAltitude; bUpdateCamPos = true; } if( bUpdateCamPos ) { mpCamera->SetPosition(m_CameraPos.x, m_CameraPos.y, m_CameraPos.z); mpCamera->Update(); } m_CameraViewMatrix = (D3DXMATRIX&)*mpCamera->GetViewMatrix(); D3DXMATRIX mProj = (D3DXMATRIX &)*mpCamera->GetProjectionMatrix(); float fEarthRadius = SAirScatteringAttribs().fEarthRadius; D3DXVECTOR3 EarthCenter(0, -fEarthRadius, 0); float fNearPlaneZ, fFarPlaneZ; ComputeApproximateNearFarPlaneDist(m_CameraPos, m_CameraViewMatrix, mProj, EarthCenter, fEarthRadius, fEarthRadius + m_fMinElevation, fEarthRadius + m_fMaxElevation, fNearPlaneZ, fFarPlaneZ); fNearPlaneZ = max(fNearPlaneZ, 50); fFarPlaneZ = max(fFarPlaneZ, fNearPlaneZ+100); fFarPlaneZ = max(fFarPlaneZ, 1000); mpCamera->SetNearPlaneDistance( fNearPlaneZ ); mpCamera->SetFarPlaneDistance( fFarPlaneZ ); mpCamera->Update(); } static D3DXVECTOR2 ProjToUV(const D3DXVECTOR2& f2ProjSpaceXY) { return D3DXVECTOR2(0.5f + 0.5f*f2ProjSpaceXY.x, 0.5f - 0.5f*f2ProjSpaceXY.y); } // DirectX 11 render callback //----------------------------------------------------------------------------- void COutdoorLightScatteringSample::Render(double deltaSeconds) { const float srgbClearColor[] = { 0.0993f, 0.0993f, 0.0993f, 1.0f }; //sRGB - red,green,blue,alpha pow(0.350, 2.2) const float rgbClearColor[] = { 0.350f, 0.350f, 0.350f, 1.0f }; //RGB - red,green,blue,alpha // Clear back buffer const float clearColor[] = { 0.0993f, 0.0993f, 0.0993f, 1.0f }; mpContext->ClearRenderTargetView( mpBackBufferRTV, clearColor ); mpContext->ClearDepthStencilView( mpDepthStencilView, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 0.0f, 0); CPUTRenderParametersDX drawParams(mpContext); D3DXMATRIX mProj = (D3DXMATRIX &)*mpCamera->GetProjectionMatrix(); D3DXMATRIX mView = m_CameraViewMatrix;//(D3DXMATRIX &)*mpCamera->GetViewMatrix(); D3DXMATRIX mViewProj = mView * mProj; // Get the camera position D3DXMATRIX CameraWorld; D3DXMatrixInverse(&CameraWorld, NULL, &mView); D3DXVECTOR3 CameraPos = *(D3DXVECTOR3*)&CameraWorld._41; D3DXVECTOR3 v3LightDir = -(D3DXVECTOR3&)m_pDirLightOrienationCamera->GetLook(); D3DXVECTOR3 v3DirOnLight = -v3LightDir; SLightAttribs LightAttribs; LightAttribs.f4DirOnLight = D3DXVECTOR4( v3DirOnLight.x, v3DirOnLight.y, v3DirOnLight.z, 0 ); D3DXVECTOR4 f4ExtraterrestrialSunColor = D3DXVECTOR4(10,10,10,10); LightAttribs.f4ExtraterrestrialSunColor = f4ExtraterrestrialSunColor*m_fScatteringScale; mLightColor = (float3&)f4ExtraterrestrialSunColor; CPUTGuiControllerDX11* pGUI = CPUTGetGuiController(); UINT uiSelectedItem; ((CPUTDropdown*)pGUI->GetControl(ID_FIRST_CASCADE_TO_RAY_MARCH_DROPDOWN))->GetSelectedItem(uiSelectedItem); m_PPAttribs.m_iFirstCascade = min((int)uiSelectedItem, m_TerrainRenderParams.m_iNumShadowCascades - 1); m_PPAttribs.m_fFirstCascade = (float)m_PPAttribs.m_iFirstCascade; RenderShadowMap(mpContext, LightAttribs); LightAttribs.ShadowAttribs.bVisualizeCascades = ((CPUTCheckbox*)pGUI->GetControl(ID_SHOW_CASCADES_CHECK))->GetCheckboxState() == CPUT_CHECKBOX_CHECKED; // Calculate location of the sun on the screen D3DXVECTOR4 &f4LightPosPS = LightAttribs.f4LightScreenPos; D3DXVec4Transform(&f4LightPosPS, &LightAttribs.f4DirOnLight, &mViewProj); f4LightPosPS.x /= f4LightPosPS.w; f4LightPosPS.y /= f4LightPosPS.w; f4LightPosPS.z /= f4LightPosPS.w; float fDistToLightOnScreen = D3DXVec2Length( (D3DXVECTOR2*)&f4LightPosPS ); float fMaxDist = 100; if( fDistToLightOnScreen > fMaxDist ) (D3DXVECTOR2&)f4LightPosPS *= fMaxDist/fDistToLightOnScreen; // Note that in fact the outermost visible screen pixels do not lie exactly on the boundary (+1 or -1), but are biased by // 0.5 screen pixel size inwards. Using these adjusted boundaries improves precision and results in // smaller number of pixels which require inscattering correction LightAttribs.bIsLightOnScreen = abs(f4LightPosPS.x) <= 1.f - 1.f/(float)m_uiBackBufferWidth && abs(f4LightPosPS.y) <= 1.f - 1.f/(float)m_uiBackBufferHeight; UpdateConstantBuffer(mpContext, m_pcbLightAttribs, &LightAttribs, sizeof(LightAttribs)); if( m_bEnableLightScattering ) { float pClearColor[4] = {0,0,0,0}; m_pOffscreenRenderTarget->SetRenderTarget( drawParams, m_pOffscreenDepth, 0, pClearColor, true, 0.f ); } else { mpContext->ClearRenderTargetView( mpBackBufferRTV, srgbClearColor ); mpContext->ClearDepthStencilView(mpDepthStencilView, D3D11_CLEAR_DEPTH, 0.0f, 0); } // Render terrain ID3D11Buffer *pcMediaScatteringParams = m_pLightSctrPP->GetMediaAttribsCB(); ID3D11ShaderResourceView *pPrecomputedNetDensitySRV = m_pLightSctrPP->GetPrecomputedNetDensitySRV(); ID3D11ShaderResourceView *pAmbientSkyLightSRV = m_pLightSctrPP->GetAmbientSkyLightSRV(mpD3dDevice, mpContext); m_EarthHemisphere.Render( mpContext, m_CameraPos, mViewProj, m_pcbLightAttribs, pcMediaScatteringParams, m_pShadowMapSRV, pPrecomputedNetDensitySRV, pAmbientSkyLightSRV, false); if( m_bEnableLightScattering ) { D3DXMATRIX mViewProjInverseMatr; D3DXMatrixInverse(&mViewProjInverseMatr, NULL, &mViewProj); SFrameAttribs FrameAttribs; FrameAttribs.pd3dDevice = mpD3dDevice; FrameAttribs.pd3dDeviceContext = mpContext; FrameAttribs.dElapsedTime = deltaSeconds; FrameAttribs.pLightAttribs = &LightAttribs; m_PPAttribs.m_iNumCascades = m_TerrainRenderParams.m_iNumShadowCascades; m_PPAttribs.m_fNumCascades = (float)m_TerrainRenderParams.m_iNumShadowCascades; CPUTGuiControllerDX11* pGUI = CPUTGetGuiController(); CPUTSlider* pSlider = static_cast(pGUI->GetControl(ID_REFINEMENT_THRESHOLD)); pSlider->GetValue(m_PPAttribs.m_fRefinementThreshold); FrameAttribs.CameraAttribs.f4CameraPos = D3DXVECTOR4(CameraPos.x, CameraPos.y, CameraPos.z, 0); ///< Camera world position FrameAttribs.CameraAttribs.fNearPlaneZ = mpCamera->GetNearPlaneDistance(); FrameAttribs.CameraAttribs.fFarPlaneZ = mpCamera->GetFarPlaneDistance() * 0.999999f; D3DXMatrixTranspose( &FrameAttribs.CameraAttribs.mViewT, &mView); D3DXMatrixTranspose( &FrameAttribs.CameraAttribs.mProjT, &mProj); D3DXMatrixTranspose( &FrameAttribs.CameraAttribs.mViewProjInvT, &mViewProjInverseMatr); FrameAttribs.pcbLightAttribs = m_pcbLightAttribs; m_PPAttribs.m_fMaxShadowMapStep = static_cast(m_uiShadowMapResolution / 4); m_PPAttribs.m_f2ShadowMapTexelSize = D3DXVECTOR2( 1.f / static_cast(m_uiShadowMapResolution), 1.f / static_cast(m_uiShadowMapResolution) ); m_PPAttribs.m_uiShadowMapResolution = m_uiShadowMapResolution; // During the ray marching, on each step we move by the texel size in either horz // or vert direction. So resolution of min/max mipmap should be the same as the // resolution of the original shadow map m_PPAttribs.m_uiMinMaxShadowMapResolution = m_uiShadowMapResolution; FrameAttribs.ptex2DSrcColorBufferSRV = m_pOffscreenRenderTarget->GetColorResourceView(); FrameAttribs.ptex2DSrcColorBufferRTV = m_pOffscreenRenderTarget->GetActiveRenderTargetView(); FrameAttribs.ptex2DSrcDepthBufferSRV = m_pOffscreenDepth->GetDepthResourceView(); FrameAttribs.ptex2DSrcDepthBufferDSV = m_pOffscreenDepth->GetActiveDepthStencilView(); FrameAttribs.ptex2DShadowMapSRV = m_pShadowMapSRV; FrameAttribs.pDstRTV = mpBackBufferRTV; // Then perform the post processing, swapping the inverseworld view projection matrix axes. m_pLightSctrPP->PerformPostProcessing(FrameAttribs, m_PPAttribs); m_pOffscreenRenderTarget->RestoreRenderTarget(drawParams); } // Draw GUI // if( m_iGUIMode ) CPUTDrawGUI(); } // Handle the shutdown event - clean up everything you created //----------------------------------------------------------------------------- void COutdoorLightScatteringSample::Shutdown() { CPUT_DX11::Shutdown(); } // Entrypoint for your sample //----------------------------------------------------------------------------- int WINAPI wWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow ) { UNREFERENCED_PARAMETER(hInstance); UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(lpCmdLine); UNREFERENCED_PARAMETER(nCmdShow); // tell VS to report leaks at any exit of the program _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); CPUTResult result=CPUT_SUCCESS; int returnCode=0; // create an instance of my sample COutdoorLightScatteringSample* sample = new COutdoorLightScatteringSample(); // Initialize the system and give it the base CPUT resource directory (location of GUI images/etc) sample->CPUTInitialize(_L("CPUT//resources//")); // window parameters CPUTWindowCreationParams params; params.startFullscreen = false; params.windowPositionX = 64; params.windowPositionY = 64; // device parameters params.deviceParams.refreshRate = 60; params.deviceParams.swapChainBufferCount= 1; params.deviceParams.swapChainFormat = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB; params.deviceParams.swapChainUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT | DXGI_USAGE_SHADER_INPUT; // parse out the parameter settings cString AssetFilename_NotUsed; cString CommandLine(lpCmdLine); sample->CPUTParseCommandLine(CommandLine, ¶ms, &AssetFilename_NotUsed); // create the window and device context result = sample->CPUTCreateWindowAndContext(_L("Outdoor Light Scattering Sample"), params); ASSERT( CPUTSUCCESS(result), _L("CPUT Error creating window and context.") ); // start the main message loop returnCode = sample->CPUTMessageLoop(); sample->DeviceShutdown(); // cleanup resources delete sample; // exit return returnCode; } ================================================ FILE: OutdoorLightScattering.h ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #pragma once #include #include "Structures.fxh" // Temporary: #undef float2 #undef float3 #undef float4 #include #include "CPUT_DX11.h" #include "CPUTMaterial.h" #include // for D3D11_BUFFER_DESC #include // for XMFLOAT #include // for srand(time) #include "EarthHemisphere.h" #include "ElevationDataSource.h" struct FrameConstantBuffer { XMFLOAT4 Eye; XMFLOAT4 LookAt; XMFLOAT4 Up; XMFLOAT4 LightDirection; XMMATRIX worldMatrix; XMMATRIX viewMatrix; XMMATRIX projectionMatrix; }; static const CPUTControlID ID_MAIN_PANEL = 10; static const CPUTControlID ID_ADDITIONAL_ATTRIBS_PANEL = 20; static const CPUTControlID ID_TONE_MAPPING_ATTRIBS_PANEL = 30; static const CPUTControlID ID_HELP_TEXT_PANEL = 40; static const CPUTControlID ID_IGNORE_CONTROL_ID = -1; static const CPUTControlID CONTROL_PANEL_IDS[] = {ID_MAIN_PANEL, ID_ADDITIONAL_ATTRIBS_PANEL, ID_TONE_MAPPING_ATTRIBS_PANEL}; enum CONTROL_IDS { ID_SELECT_PANEL_COMBO = 100, ID_FULLSCREEN_BUTTON, ID_ENABLE_VSYNC, ID_ENABLE_LIGHT_SCATTERING, ID_ENABLE_LIGHT_SHAFTS, ID_ANIMATE_SUN, ID_LIGHT_SCTR_TECHNIQUE, ID_NUM_INTEGRATION_STEPS, ID_NUM_EPIPOLAR_SLICES, ID_NUM_SAMPLES_IN_EPIPOLAR_SLICE, ID_INITIAL_SAMPLE_STEP_IN_EPIPOLAR_SLICE, ID_EPIPOLE_SAMPLING_DENSITY_FACTOR, ID_REFINEMENT_THRESHOLD, ID_SHOW_SAMPLING, ID_MIN_MAX_SHADOW_MAP_OPTIMIZATION, ID_OPTIMIZE_SAMPLE_LOCATIONS, ID_CORRECT_SCATTERING_AT_DEPTH_BREAKS, ID_SCATTERING_SCALE, ID_MIDDLE_GRAY, ID_WHITE_POINT, ID_LUM_SATURATION, ID_AUTO_EXPOSURE, ID_TONE_MAPPING_MODE, ID_LIGHT_ADAPTATION, ID_SHOW_DEPTH_BREAKS, ID_SHOW_LIGHTING_ONLY_CHECK, ID_SHADOW_MAP_RESOLUTION, ID_USE_CUSTOM_SCTR_COEFFS_CHECK, ID_RLGH_COLOR_BTN, ID_MIE_COLOR_BTN, ID_SINGLE_SCTR_MODE_DROPDOWN, ID_MULTIPLE_SCTR_MODE_DROPDOWN, ID_NUM_CASCADES_DROPDOWN, ID_SHOW_CASCADES_CHECK, ID_SMOOTH_SHADOWS_CHECK, ID_BEST_CASCADE_SEARCH_CHECK, ID_CASCADE_PARTITIONING_SLIDER, ID_CASCADE_PROCESSING_MODE_DROPDOWN, ID_FIRST_CASCADE_TO_RAY_MARCH_DROPDOWN, ID_REFINEMENT_CRITERION_DROPDOWN, ID_EXTINCTION_EVAL_MODE_DROPDOWN, ID_MIN_MAX_MIP_FORMAT_DROPDOWN, ID_AEROSOL_DENSITY_SCALE_SLIDER, ID_AEROSOL_ABSORBTION_SCALE_SLIDER, ID_TEXTLINES = 1000 }; // DirectX 11 Sample //----------------------------------------------------------------------------- class COutdoorLightScatteringSample:public CPUT_DX11 { public: COutdoorLightScatteringSample(); virtual ~COutdoorLightScatteringSample(); // Event handling virtual CPUTEventHandledCode HandleKeyboardEvent(CPUTKey key); virtual CPUTEventHandledCode HandleMouseEvent(int x, int y, int wheel, CPUTMouseState state); virtual void HandleCallbackEvent( CPUTEventID Event, CPUTControlID ControlID, CPUTControl* pControl ); // 'callback' handlers for rendering events. Derived from CPUT_DX11 virtual void Create(); virtual void Render(double deltaSeconds); virtual void Update(double deltaSeconds); virtual void ResizeWindow(UINT width, UINT height); void Shutdown(); HRESULT ParseConfigurationFile( LPCWSTR ConfigFilePath ); private: HRESULT CreateShadowMap(ID3D11Device* pd3dDevice); void ReleaseShadowMap(); HRESULT CreateTmpBackBuffAndDepthBuff(ID3D11Device* pd3dDevice); void ReleaseTmpBackBuffAndDepthBuff(); void RenderShadowMap(ID3D11DeviceContext *pContext, SLightAttribs &LightAttribs); void Destroy(); float GetSceneExtent(); class CLightSctrPostProcess *m_pLightSctrPP; UINT m_uiShadowMapResolution; float m_fCascadePartitioningFactor; bool m_bEnableLightScattering; bool m_bAnimateSun; static const int m_iMinEpipolarSlices = 32; static const int m_iMaxEpipolarSlices = 2048; static const int m_iMinSamplesInEpipolarSlice = 32; static const int m_iMaxSamplesInEpipolarSlice = 2048; static const int m_iMaxEpipoleSamplingDensityFactor = 32; static const int m_iMinInitialSamplesInEpipolarSlice = 8; SPostProcessingAttribs m_PPAttribs; float m_fScatteringScale; std::vector< CComPtr > m_pShadowMapDSVs; CComPtr m_pShadowMapSRV; CPUTRenderTargetColor* m_pOffscreenRenderTarget; CPUTRenderTargetDepth* m_pOffscreenDepth; CPUTCamera* m_pDirectionalLightCamera; CPUTCamera* m_pDirLightOrienationCamera; CPUTCameraController* mpCameraController; CPUTCameraController* m_pLightController; CPUTTimerWin m_Timer; float m_fElapsedTime; D3DXVECTOR4 m_f4LightColor; int m_iGUIMode; SRenderingParams m_TerrainRenderParams; std::wstring m_strRawDEMDataFile; std::wstring m_strMtrlMaskFile; std::wstring m_strTileTexPaths[CEarthHemsiphere::NUM_TILE_TEXTURES]; std::wstring m_strNormalMapTexPaths[CEarthHemsiphere::NUM_TILE_TEXTURES]; std::auto_ptr m_pElevDataSource; CEarthHemsiphere m_EarthHemisphere; D3DXMATRIX m_CameraViewMatrix; D3DXVECTOR3 m_CameraPos; CComPtr m_pcbLightAttribs; UINT m_uiBackBufferWidth, m_uiBackBufferHeight; CPUTDropdown* m_pSelectPanelDropDowns[3]; UINT m_uiSelectedPanelInd; float m_fMinElevation, m_fMaxElevation; private: COutdoorLightScatteringSample(const COutdoorLightScatteringSample&); const COutdoorLightScatteringSample& operator = (const COutdoorLightScatteringSample&); }; ================================================ FILE: OutdoorLightScattering_2010.sln ================================================  Microsoft Visual Studio Solution File, Format Version 11.00 # Visual Studio 2010 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "OutdoorLightScatteringSample_2010", "OutdoorLightScattering_2010.vcxproj", "{168ECB7C-9ECB-42FE-809E-28A0B3428F5F}" ProjectSection(ProjectDependencies) = postProject {8B2DDEDC-A574-4B24-AEC5-03949B5F57BD} = {8B2DDEDC-A574-4B24-AEC5-03949B5F57BD} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CPUT-DX11", "CPUT\CPUT-DX11.vcxproj", "{8B2DDEDC-A574-4B24-AEC5-03949B5F57BD}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 Debug|x64 = Debug|x64 Release|Win32 = Release|Win32 Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {168ECB7C-9ECB-42FE-809E-28A0B3428F5F}.Debug|Win32.ActiveCfg = Debug|Win32 {168ECB7C-9ECB-42FE-809E-28A0B3428F5F}.Debug|Win32.Build.0 = Debug|Win32 {168ECB7C-9ECB-42FE-809E-28A0B3428F5F}.Debug|x64.ActiveCfg = Debug|x64 {168ECB7C-9ECB-42FE-809E-28A0B3428F5F}.Debug|x64.Build.0 = Debug|x64 {168ECB7C-9ECB-42FE-809E-28A0B3428F5F}.Release|Win32.ActiveCfg = Release|Win32 {168ECB7C-9ECB-42FE-809E-28A0B3428F5F}.Release|Win32.Build.0 = Release|Win32 {168ECB7C-9ECB-42FE-809E-28A0B3428F5F}.Release|x64.ActiveCfg = Release|x64 {168ECB7C-9ECB-42FE-809E-28A0B3428F5F}.Release|x64.Build.0 = Release|x64 {8B2DDEDC-A574-4B24-AEC5-03949B5F57BD}.Debug|Win32.ActiveCfg = Debug|Win32 {8B2DDEDC-A574-4B24-AEC5-03949B5F57BD}.Debug|Win32.Build.0 = Debug|Win32 {8B2DDEDC-A574-4B24-AEC5-03949B5F57BD}.Debug|x64.ActiveCfg = Debug|x64 {8B2DDEDC-A574-4B24-AEC5-03949B5F57BD}.Debug|x64.Build.0 = Debug|x64 {8B2DDEDC-A574-4B24-AEC5-03949B5F57BD}.Release|Win32.ActiveCfg = Release|Win32 {8B2DDEDC-A574-4B24-AEC5-03949B5F57BD}.Release|Win32.Build.0 = Release|Win32 {8B2DDEDC-A574-4B24-AEC5-03949B5F57BD}.Release|x64.ActiveCfg = Release|x64 {8B2DDEDC-A574-4B24-AEC5-03949B5F57BD}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal ================================================ FILE: OutdoorLightScattering_2010.vcxproj ================================================  Debug Win32 Debug x64 Profile Win32 Profile x64 Release Win32 Release x64 Use Use Use Use Use Use Use Use Use Use Use Use Use Use Use Use Use Use Use Use Use Use Use Use Use Use Use Use Use Use Create Create Create Create Create Create Document fxc /I ..\Core\effects /T fx_5_0 %(FullPath) /Fc $(IntDir)fx_asm\%(Filename).asm $(IntDir)fx_asm\%(Filename).asm;%(Outputs) Document fxc /I ..\Core\effects /T fx_5_0 %(FullPath) /Fc $(IntDir)fx_asm\%(Filename).asm $(IntDir)fx_asm\%(Filename).asm;%(Outputs) Document fxc /I ..\Core\effects /T fx_5_0 %(FullPath) /Fc $(IntDir)fx_asm\%(Filename).asm $(IntDir)fx_asm\%(Filename).asm;%(Outputs) {8b2ddedc-a574-4b24-aec5-03949b5f57bd} {168ECB7C-9ECB-42FE-809E-28A0B3428F5F} Win32Proj SampleStartDX OutdoorLightScatteringSample Application true Unicode Application true Unicode Application false true Unicode Application false true Unicode Application false true Unicode Application false true Unicode true .\;.\Terrain;.\fx;$(DXSDK_DIR)Include;$(IncludePath);CPUT\CPUT;..\Effects11\Inc;..\Effects11 $(LibraryPath);$(DXSDK_DIR)Lib\x86 $(SolutionDir)bin\$(Configuration)\$(Platform)\ bin\$(Configuration)\$(Platform)\ $(DXSDK_DIR)Utilities\bin\x86;$(ExecutablePath) true .\;.\Terrain;.\fx;$(DXSDK_DIR)Include;$(IncludePath);CPUT\CPUT;..\Effects11\Inc;..\Effects11 $(LibraryPath);$(DXSDK_DIR)Lib\x64; $(SolutionDir)bin\$(Configuration)\$(Platform)\ bin\$(Configuration)\$(Platform)\ $(DXSDK_DIR)Utilities\bin\x64;$(ExecutablePath) false .\;.\Terrain;.\fx;$(DXSDK_DIR)Include;$(IncludePath);CPUT\CPUT;..\Effects11\Inc;..\Effects11 $(LibraryPath);$(DXSDK_DIR)Lib\x86 $(SolutionDir)bin\$(Configuration)\$(Platform)\ bin\$(Configuration)\$(Platform)\ $(DXSDK_DIR)Utilities\bin\x86;$(ExecutablePath) false .\;.\Terrain;.\fx;$(DXSDK_DIR)Include;$(IncludePath);CPUT\CPUT;$(GPA_INCLUDE_DIR);..\Effects11\Inc;..\Effects11;..\Effects11\Inc $(LibraryPath);$(DXSDK_DIR)Lib\x86;$(GPA_LIBRARY_DIR)\x86 $(SolutionDir)bin\$(Configuration)\$(Platform)\ bin\$(Configuration)\$(Platform)\ $(DXSDK_DIR)Utilities\bin\x86;$(ExecutablePath) false .\;.\Terrain;.\fx;$(DXSDK_DIR)Include;$(IncludePath);CPUT\CPUT;..\Effects11\Inc;..\Effects11 $(LibraryPath);$(DXSDK_DIR)Lib\x64; $(SolutionDir)bin\$(Configuration)\$(Platform)\ bin\$(Configuration)\$(Platform)\ $(DXSDK_DIR)Utilities\bin\x64;$(ExecutablePath) false .\;.\Terrain;.\fx;$(DXSDK_DIR)Include;$(IncludePath);CPUT\CPUT;$(GPA_INCLUDE_DIR);..\Effects11\Inc;..\Effects11;..\Effects11\Inc $(LibraryPath);$(DXSDK_DIR)Lib\x64;$(GPA_LIBRARY_DIR)\x64 $(SolutionDir)bin\$(Configuration)\$(Platform)\ bin\$(Configuration)\$(Platform)\ $(DXSDK_DIR)Utilities\bin\x64;$(ExecutablePath) Level3 Disabled WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) Windows true d3d11.lib;d3dx11.lib;d3dcompiler.lib;d3dx11d.lib;d3dx9d.lib;dxerr.lib;dxguid.lib;%(AdditionalDependencies) Level3 Disabled WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) MultiThreadedDebugDLL Windows true d3d11.lib;d3dcompiler.lib;d3dx11d.lib;d3dx9d.lib;dxerr.lib;dxguid.lib;%(AdditionalDependencies) Level3 MaxSpeed true true WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) Windows true true true d3d11.lib;d3dx11.lib;d3dcompiler.lib;d3dx11.lib;d3dx9.lib;dxerr.lib;dxguid.lib;%(AdditionalDependencies) false Level3 MaxSpeed true true WIN32;NDEBUG;_WINDOWS;CPUT_GPA_INSTRUMENTATION;%(PreprocessorDefinitions) Windows true true true d3d11.lib;d3dx11.lib;d3dcompiler.lib;d3dx11.lib;d3dx9.lib;dxerr.lib;dxguid.lib;d3d9.lib;gpasdk_s.lib;%(AdditionalDependencies) Level3 MaxSpeed true true WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) MultiThreadedDLL Windows true true true d3d11.lib;d3dx11.lib;d3dcompiler.lib;d3dx11.lib;d3dx9.lib;dxerr.lib;dxguid.lib;%(AdditionalDependencies) Level3 MaxSpeed true true WIN32;NDEBUG;_WINDOWS;CPUT_GPA_INSTRUMENTATION;%(PreprocessorDefinitions) Windows true true true d3d11.lib;d3dx11.lib;d3dcompiler.lib;d3dx11.lib;d3dx9.lib;dxerr.lib;dxguid.lib;d3d9.lib;gpasdk_s.lib;%(AdditionalDependencies) ================================================ FILE: OutdoorLightScattering_2010.vcxproj.filters ================================================  {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx {c95fe20e-27c4-40a1-8a03-55ccad7902ba} {95d79d31-6ee9-4479-9563-d42d7b270e31} {02e11d75-b0cc-4ec1-b99a-daa6c657d6c7} {b001a578-6ac0-43ae-962b-0403aea00dc5} {60d679d6-0738-4634-a1b4-4072d0924537} Sample Files Sample Files Sample Files Sample Files Sample Files\Terrain\Source Sample Files\Terrain\Source Sample Files\Terrain\Source Sample Files Sample Files Sample Files Sample Files Sample Files\Terrain\Include Sample Files\Terrain\Include Sample Files\Terrain\Include Sample Files\Terrain\Include Sample Files\Terrain\Include Resource Files Resource Files Sample Files\Terrain\fx Shaders Shaders Resource Files Shaders Sample Files\Terrain\fx Shaders ================================================ FILE: OutdoorLightScattering_2012.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2012 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "OutdoorLightScattering_2012", "OutdoorLightScattering_2012.vcxproj", "{08459B5C-581A-4041-AD84-32490B6D0CEB}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CPUT-DX11", "CPUT\CPUT-DX11_2012.vcxproj", "{8B2DDEDC-A574-4B24-AEC5-03949B5F57BD}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 Debug|x64 = Debug|x64 Release|Win32 = Release|Win32 Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {08459B5C-581A-4041-AD84-32490B6D0CEB}.Debug|Win32.ActiveCfg = Debug|Win32 {08459B5C-581A-4041-AD84-32490B6D0CEB}.Debug|Win32.Build.0 = Debug|Win32 {08459B5C-581A-4041-AD84-32490B6D0CEB}.Debug|x64.ActiveCfg = Debug|x64 {08459B5C-581A-4041-AD84-32490B6D0CEB}.Debug|x64.Build.0 = Debug|x64 {08459B5C-581A-4041-AD84-32490B6D0CEB}.Release|Win32.ActiveCfg = Release|Win32 {08459B5C-581A-4041-AD84-32490B6D0CEB}.Release|Win32.Build.0 = Release|Win32 {08459B5C-581A-4041-AD84-32490B6D0CEB}.Release|x64.ActiveCfg = Release|x64 {08459B5C-581A-4041-AD84-32490B6D0CEB}.Release|x64.Build.0 = Release|x64 {8B2DDEDC-A574-4B24-AEC5-03949B5F57BD}.Debug|Win32.ActiveCfg = Debug|Win32 {8B2DDEDC-A574-4B24-AEC5-03949B5F57BD}.Debug|Win32.Build.0 = Debug|Win32 {8B2DDEDC-A574-4B24-AEC5-03949B5F57BD}.Debug|x64.ActiveCfg = Debug|x64 {8B2DDEDC-A574-4B24-AEC5-03949B5F57BD}.Debug|x64.Build.0 = Debug|x64 {8B2DDEDC-A574-4B24-AEC5-03949B5F57BD}.Release|Win32.ActiveCfg = Release|Win32 {8B2DDEDC-A574-4B24-AEC5-03949B5F57BD}.Release|Win32.Build.0 = Release|Win32 {8B2DDEDC-A574-4B24-AEC5-03949B5F57BD}.Release|x64.ActiveCfg = Release|x64 {8B2DDEDC-A574-4B24-AEC5-03949B5F57BD}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal ================================================ FILE: OutdoorLightScattering_2012.vcxproj ================================================  Debug Win32 Debug x64 Release Win32 Release x64 {08459B5C-581A-4041-AD84-32490B6D0CEB} Win32Proj LightScattering_2012 Application true v110 Unicode Application true v110 Unicode Application false v110 true Unicode Application false v110 true Unicode true $(SolutionDir)bin\$(Configuration)\$(Platform)\ bin\$(Configuration)\$(Platform)\ .\;.\fx;.\Terrain;$(VCInstallDir)include;$(VCInstallDir)atlmfc\include;$(WindowsSDK_IncludePath);$(DXSDK_DIR)Include;CPUT\CPUT;..\Effects11\Inc;..\Effects11 $(DXSDK_DIR)Lib\x86;$(VCInstallDir)lib;$(VCInstallDir)atlmfc\lib;$(WindowsSDK_LibraryPath_x86); $(ProjectName)_D32 true $(SolutionDir)bin\$(Configuration)\$(Platform)\ bin\$(Configuration)\$(Platform)\ .\;.\fx;.\Terrain;$(VCInstallDir)include;$(VCInstallDir)atlmfc\include;$(WindowsSDK_IncludePath);$(DXSDK_DIR)Include;CPUT\CPUT;..\Effects11\Inc;..\Effects11 $(DXSDK_DIR)Lib\x64;$(VCInstallDir)lib\amd64;$(VCInstallDir)atlmfc\lib\amd64;$(WindowsSDK_LibraryPath_x64); $(ProjectName)_D64 false $(SolutionDir)bin\$(Configuration)\$(Platform)\ bin\$(Configuration)\$(Platform)\ .\;.\fx;.\Terrain;$(VCInstallDir)include;$(VCInstallDir)atlmfc\include;$(WindowsSDK_IncludePath);$(DXSDK_DIR)Include;CPUT\CPUT;..\Effects11\Inc;..\Effects11 $(DXSDK_DIR)Lib\x86;$(VCInstallDir)lib;$(VCInstallDir)atlmfc\lib;$(WindowsSDK_LibraryPath_x86); $(ProjectName)_R32 false $(SolutionDir)bin\$(Configuration)\$(Platform)\ bin\$(Configuration)\$(Platform)\ .\;.\fx;.\Terrain;$(VCInstallDir)include;$(VCInstallDir)atlmfc\include;$(WindowsSDK_IncludePath);$(DXSDK_DIR)Include;CPUT\CPUT;..\Effects11\Inc;..\Effects11 $(DXSDK_DIR)Lib\x64;$(VCInstallDir)lib\amd64;$(VCInstallDir)atlmfc\lib\amd64;$(WindowsSDK_LibraryPath_x64); $(ProjectName)_R64 Level3 Disabled WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) Windows true d3d11.lib;d3dx11.lib;d3dcompiler.lib;d3dx11d.lib;d3dx9d.lib;dxerr.lib;dxguid.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) Level3 Disabled WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) Windows true d3d11.lib;d3dcompiler.lib;d3dx11d.lib;d3dx9d.lib;dxerr.lib;dxguid.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) Level3 MaxSpeed true true WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) Windows true true true d3d11.lib;d3dx11.lib;d3dcompiler.lib;d3dx11.lib;d3dx9.lib;dxerr.lib;dxguid.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) Level3 MaxSpeed true true WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) Windows true true true d3d11.lib;d3dx11.lib;d3dcompiler.lib;d3dx11.lib;d3dx9.lib;dxerr.lib;dxguid.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) Document Document Document Create Create Create Create {8b2ddedc-a574-4b24-aec5-03949b5f57bd} ================================================ FILE: OutdoorLightScattering_2012.vcxproj.filters ================================================  {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx {a449af6a-af21-4134-9018-aa784f199ca8} {cfd4dd41-e415-4640-87b2-5ff0e71f2c7a} {1071fe35-d241-48f9-80e9-f8cc34bcd67b} {abc3e307-fb25-4ab4-92d9-a6dcfcfe1ef1} {b182bc3b-b482-46c0-a1a9-797bd32b980c} Resource Files Resource Files Sample Files\Terrain\fx Sample Files\Shaders Sample Files\Shaders Sample Files\Shaders Sample Files\Shaders Sample Files\Terrain\fx Sample Files\Terrain\Include Sample Files\Terrain\Include Sample Files\Terrain\Include Sample Files\Terrain\Include Sample Files\Terrain\Include Sample Files Sample Files Sample Files Sample Files Sample Files Sample Files Sample Files\Terrain\Source Sample Files\Terrain\Source Sample Files\Terrain\Source Sample Files\Terrain\Source Sample Files Sample Files Sample Files Resource Files ================================================ FILE: RenderTechnique.cpp ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "RenderTechnique.h" #include #include #include CRenderTechnique::CRenderTechnique(void) { } CRenderTechnique::~CRenderTechnique(void) { } void CRenderTechnique::Release() { m_pVS.Release(); m_pGS.Release(); m_pPS.Release(); m_pCS.Release(); m_pRS.Release(); m_pDS.Release(); m_pBS.Release(); m_pVSByteCode.Release(); m_pContext.Release(); m_pDevice.Release(); } static HRESULT CompileShaderFromFile(LPCTSTR strFilePath, LPCSTR strFunctionName, const D3D_SHADER_MACRO* pDefines, LPCSTR profile, ID3DBlob **ppBlobOut) { DWORD dwShaderFlags = D3D10_SHADER_ENABLE_STRICTNESS; #if defined( DEBUG ) || defined( _DEBUG ) // Set the D3D10_SHADER_DEBUG flag to embed debug information in the shaders. // Setting this flag improves the shader debugging experience, but still allows // the shaders to be optimized and to run exactly the way they will run in // the release configuration of this program. dwShaderFlags |= D3D10_SHADER_DEBUG; #else // Warning: do not use this flag as it causes shader compiler to fail the compilation and // report strange errors: // dwShaderFlags |= D3D10_SHADER_OPTIMIZATION_LEVEL3; #endif HRESULT hr; do { CComPtr errors; hr = D3DX11CompileFromFile(strFilePath, pDefines, NULL, strFunctionName, profile, dwShaderFlags, 0, NULL, ppBlobOut, &errors, NULL); if( errors ) { OutputDebugStringA((char*) errors->GetBufferPointer()); if( FAILED(hr) && IDRETRY != MessageBoxA(NULL, (char*) errors->GetBufferPointer(), "FX Error", MB_ICONERROR|MB_ABORTRETRYIGNORE) ) { break; } } } while( FAILED(hr) ); return hr; } HRESULT CRenderTechnique::CreateVertexShaderFromFile(LPCTSTR strFilePath, LPSTR strFunctionName, const D3D_SHADER_MACRO* pDefines) { HRESULT hr; m_pVSByteCode.Release(); hr = CompileShaderFromFile( strFilePath, strFunctionName, pDefines, "vs_5_0", &m_pVSByteCode ); if(FAILED(hr))return hr; m_pVS.Release(); if( m_pDevice ) hr = m_pDevice->CreateVertexShader( m_pVSByteCode->GetBufferPointer(), m_pVSByteCode->GetBufferSize(), NULL, &m_pVS ); else return E_FAIL; return hr; } HRESULT CRenderTechnique::CreateGeometryShaderFromFile(LPCTSTR strFilePath, LPSTR strFunctionName, const D3D_SHADER_MACRO* pDefines) { CComPtr pShaderByteCode; HRESULT hr; hr = CompileShaderFromFile( strFilePath, strFunctionName, pDefines, "gs_5_0", &pShaderByteCode ); if(FAILED(hr))return hr; m_pGS.Release(); if( m_pDevice ) hr = m_pDevice->CreateGeometryShader( pShaderByteCode->GetBufferPointer(), pShaderByteCode->GetBufferSize(), NULL, &m_pGS ); else return E_FAIL; return hr; } HRESULT CRenderTechnique::CreatePixelShaderFromFile(LPCTSTR strFilePath, LPSTR strFunctionName, const D3D_SHADER_MACRO* pDefines) { CComPtr pShaderByteCode; HRESULT hr; hr = CompileShaderFromFile( strFilePath, strFunctionName, pDefines, "ps_5_0", &pShaderByteCode ); if(FAILED(hr))return hr; m_pPS.Release(); if( m_pDevice ) hr = m_pDevice->CreatePixelShader( pShaderByteCode->GetBufferPointer(), pShaderByteCode->GetBufferSize(), NULL, &m_pPS ); else return E_FAIL; return hr; } HRESULT CRenderTechnique::CreateComputeShaderFromFile(LPCTSTR strFilePath, LPSTR strFunctionName, const D3D_SHADER_MACRO* pDefines) { CComPtr pShaderByteCode; HRESULT hr; hr = CompileShaderFromFile( strFilePath, strFunctionName, pDefines, "cs_5_0", &pShaderByteCode ); if(FAILED(hr))return hr; m_pCS.Release(); if( m_pDevice ) hr = m_pDevice->CreateComputeShader( pShaderByteCode->GetBufferPointer(), pShaderByteCode->GetBufferSize(), NULL, &m_pCS ); else return E_FAIL; return hr; } HRESULT CRenderTechnique::CreateVGPShadersFromFile(LPCTSTR strFilePath, LPSTR strVSFunctionName, LPSTR strGSFunctionName, LPSTR strPSFunctionName, const D3D_SHADER_MACRO* pDefines) { HRESULT hr = S_OK; if( strVSFunctionName ) { hr = CreateVertexShaderFromFile(strFilePath, strVSFunctionName, pDefines); if( FAILED(hr) )return hr; } if( strPSFunctionName ) { hr = CreatePixelShaderFromFile(strFilePath, strPSFunctionName, pDefines); if( FAILED(hr) )return hr; } if( strGSFunctionName ) { hr = CreateGeometryShaderFromFile(strFilePath, strGSFunctionName, pDefines); if( FAILED(hr) )return hr; } return hr; } void CRenderTechnique::Apply() { m_pContext->HSSetShader(NULL, NULL, 0); m_pContext->DSSetShader(NULL, NULL, 0); m_pContext->VSSetShader(m_pVS, NULL, 0); m_pContext->GSSetShader(m_pGS, NULL, 0); m_pContext->PSSetShader(m_pPS, NULL, 0); m_pContext->CSSetShader(m_pCS, NULL, 0); m_pContext->RSSetState(m_pRS); m_pContext->OMSetDepthStencilState(m_pDS, m_uiSampleRef); float fBlendFactor[] = {0, 0, 0, 0}; m_pContext->OMSetBlendState(m_pBS, fBlendFactor, 0xFFFFFFFF); } HRESULT CRenderTechnique::CreateDefaultBlendState() { HRESULT hr; D3D11_BLEND_DESC DefaultBlendStateDesc; ZeroMemory(&DefaultBlendStateDesc, sizeof(DefaultBlendStateDesc)); DefaultBlendStateDesc.IndependentBlendEnable = FALSE; for(int i=0; i< _countof(DefaultBlendStateDesc.RenderTarget); i++) DefaultBlendStateDesc.RenderTarget[i].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; V( m_pDevice->CreateBlendState( &DefaultBlendStateDesc, &m_pBS) ); return hr; } HRESULT CRenderTechnique::CreateDefaultDepthState(BOOL bEnableDepth, D3D11_DEPTH_WRITE_MASK WriteMask) { HRESULT hr; D3D11_DEPTH_STENCIL_DESC DSDesc; ZeroMemory(&DSDesc, sizeof(DSDesc)); DSDesc.DepthEnable = bEnableDepth; DSDesc.DepthWriteMask = WriteMask; DSDesc.DepthFunc = D3D11_COMPARISON_GREATER; CComPtr pDisableDepthTestDS; V( m_pDevice->CreateDepthStencilState( &DSDesc, &m_pDS) ); return hr; } HRESULT CRenderTechnique::CreateDefaultRasterizerState(D3D11_FILL_MODE FillMode, D3D11_CULL_MODE CullMode, BOOL bIsFrontCCW) { HRESULT hr; D3D11_RASTERIZER_DESC RSDesc; ZeroMemory(&RSDesc, sizeof(RSDesc)); RSDesc.FillMode = FillMode; RSDesc.CullMode = CullMode; RSDesc.FrontCounterClockwise = bIsFrontCCW; CComPtr pRSSolidFillNoCull; V( m_pDevice->CreateRasterizerState( &RSDesc, &m_pRS) ); return hr; } HRESULT CRenderTechnique::CreateSampler(ID3D11SamplerState **ppSamplerState, D3D11_FILTER Filter, D3D11_TEXTURE_ADDRESS_MODE AddressMode) { D3D11_SAMPLER_DESC SamDesc = { Filter, AddressMode, AddressMode, AddressMode, 0, //FLOAT MipLODBias; 0, //UINT MaxAnisotropy; D3D11_COMPARISON_NEVER, // D3D11_COMPARISON_FUNC ComparisonFunc; {0.f, 0.f, 0.f, 0.f}, //FLOAT BorderColor[ 4 ]; -FLT_MAX, //FLOAT MinLOD; +FLT_MAX //FLOAT MaxLOD; }; HRESULT hr = m_pDevice->CreateSamplerState(&SamDesc, ppSamplerState); return hr; } ================================================ FILE: RenderTechnique.h ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #pragma once #include #include class CRenderTechnique { public: CRenderTechnique(void); ~CRenderTechnique(void); void Release(); void SetDeviceAndContext(ID3D11Device *pDevice, ID3D11DeviceContext *pCtx){m_pDevice = pDevice; m_pContext = pCtx;} void Apply(); ID3D11VertexShader * GetVS(){return m_pVS;} ID3D11GeometryShader * GetGS(){return m_pGS;} ID3D11PixelShader * GetPS(){return m_pPS;} ID3D11ComputeShader * GetCS(){return m_pCS;} ID3D11RasterizerState * GetRS(){return m_pRS;} ID3D11DepthStencilState * GetDS(){return m_pDS;} ID3D11BlendState * GetBS(){return m_pBS;} ID3DBlob * GetVSByteCode(){return m_pVSByteCode;} void SetVS(ID3D11VertexShader *pVS ){m_pVS = pVS;} void SetGS(ID3D11GeometryShader *pGS ){m_pGS = pGS;} void SetPS(ID3D11PixelShader *pPS ){m_pPS = pPS;} void SetCS(ID3D11ComputeShader *pCS ){m_pCS = pCS;} void SetRS(ID3D11RasterizerState *pRS ){m_pRS = pRS;} void SetDS(ID3D11DepthStencilState *pDS, UINT uiSampleRef = 0 ){m_pDS = pDS; m_uiSampleRef = uiSampleRef;} void SetBS(ID3D11BlendState *pBS ){m_pBS = pBS;} HRESULT CreateVertexShaderFromFile (LPCTSTR strFilePath, LPSTR strFunctionName, const D3D_SHADER_MACRO* pDefines); HRESULT CreateGeometryShaderFromFile (LPCTSTR strFilePath, LPSTR strFunctionName, const D3D_SHADER_MACRO* pDefines); HRESULT CreatePixelShaderFromFile (LPCTSTR strFilePath, LPSTR strFunctionName, const D3D_SHADER_MACRO* pDefines); HRESULT CreateComputeShaderFromFile (LPCTSTR strFilePath, LPSTR strFunctionName, const D3D_SHADER_MACRO* pDefines); HRESULT CreateVGPShadersFromFile (LPCTSTR strFilePath, LPSTR strVSFunctionName, LPSTR strGSFunctionName, LPSTR strPSFunctionName, const D3D_SHADER_MACRO* pDefines); bool IsValid(){ return m_pDevice && m_pContext && (m_pPS || m_pCS);} HRESULT CreateDefaultBlendState(); HRESULT CreateDefaultDepthState(BOOL bEnableDepth = TRUE, D3D11_DEPTH_WRITE_MASK WriteMask = D3D11_DEPTH_WRITE_MASK_ALL); HRESULT CreateDefaultRasterizerState(D3D11_FILL_MODE FillMode = D3D11_FILL_SOLID, D3D11_CULL_MODE CullMode = D3D11_CULL_BACK, BOOL bIsFrontCCW = FALSE); HRESULT CreateSampler(ID3D11SamplerState **ppSamplerState, D3D11_FILTER Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR, D3D11_TEXTURE_ADDRESS_MODE AddressMode = D3D11_TEXTURE_ADDRESS_CLAMP); private: CComPtr m_pDevice; CComPtr m_pContext; CComPtr m_pVS; CComPtr m_pGS; CComPtr m_pPS; CComPtr m_pCS; CComPtr m_pRS; CComPtr m_pDS; CComPtr m_pBS; UINT m_uiSampleRef; CComPtr m_pVSByteCode; }; ================================================ FILE: ShaderMacroHelper.h ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #include #include #include class CD3DShaderMacroHelper { public: CD3DShaderMacroHelper() : m_bIsFinalized(false) {} template void AddShaderMacro( LPCSTR Name, DefintionType Definition ) { assert( !m_bIsFinalized ); std::ostringstream ss; ss << Definition; AddShaderMacro( Name, ss.str().c_str() ); } template<> void AddShaderMacro( LPCSTR Name, LPCSTR Definition ) { assert( !m_bIsFinalized ); D3D_SHADER_MACRO NewMacro = { Name, m_DefinitionsPull.insert(Definition).first->c_str() }; m_Macroes.push_back(NewMacro); } template<> void AddShaderMacro( LPCSTR Name, bool Definition ) { assert( !m_bIsFinalized ); AddShaderMacro( Name, Definition ? "1" : "0"); } void Finalize() { D3D_SHADER_MACRO LastMacro = {NULL, NULL}; m_Macroes.push_back(LastMacro); m_bIsFinalized = true; } operator const D3D_SHADER_MACRO* () { assert( !m_Macroes.size() || m_bIsFinalized ); if( m_Macroes.size() && !m_bIsFinalized ) Finalize(); return m_Macroes.size() ? &m_Macroes[0] : NULL; } private: std::vector< D3D_SHADER_MACRO > m_Macroes; std::set< std::string > m_DefinitionsPull; bool m_bIsFinalized; }; ================================================ FILE: Terrain/ConfigFile.cpp ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "OutdoorLightScattering.h" // Parses the string as bool value HRESULT ParseParameterBool( LPWSTR Value, bool &BoolValue ) { if( wcscmp(L"true", Value) == 0 ) BoolValue = true; else if( wcscmp(L"false", Value) == 0 ) BoolValue = false; else { CHECK_HR_RET( E_FAIL, L"\"%s\" is not correct boolean value. Only \"true\" and \"false\" are allowed", Value); } return S_OK; } // Parses the string as float value float ParseParameterFloat( LPWSTR Value ) { float FloatVal; _stscanf_s( Value, L"%f", &FloatVal ); return FloatVal; } // Parses the string as int value int ParseParameterInt( LPWSTR Value ) { int IntVal; _stscanf_s( Value, L"%d", &IntVal); return IntVal; } // Parses the string as string value void ParseParameterString( std::wstring &StringValue, FILE *pFile ) { StringValue.clear(); // Read space fgetc(pFile); while( !feof(pFile) ) { WCHAR CurrSymb = fgetc(pFile); if( CurrSymb != L'\n' && CurrSymb != 65535 ) StringValue += CurrSymb; else break; } } void ParseParameterString( LPWSTR StringValue, int MaxLen, FILE *pFile ) { // Read space fgetc(pFile); fgetws(StringValue, MaxLen, pFile); size_t len = wcslen(StringValue); // Remove \n from the end of the line if( StringValue[len-1] == L'\n' ) StringValue[len-1] = L'\0'; } // Parses the configuration file HRESULT COutdoorLightScatteringSample :: ParseConfigurationFile( LPCWSTR ConfigFilePath ) { m_strRawDEMDataFile.clear(); m_strMtrlMaskFile.clear(); FILE *pConfigFile = NULL; if( _tfopen_s( &pConfigFile, ConfigFilePath, _T("r") ) != 0 ) { CHECK_HR_RET(E_FAIL, L"Failed to open the configuration file (%s)", ConfigFilePath); } while( !feof(pConfigFile) ) { TCHAR Parameter[128]; TCHAR EqualSign[128]; _ftscanf_s( pConfigFile, _T("%s"), Parameter, _countof(Parameter)); _ftscanf_s( pConfigFile, _T("%s"), EqualSign, _countof(EqualSign)); if( wcscmp(EqualSign, L"=") != 0 ) { LOG_ERROR( L"Equal sign (=) is missing for parameter \"%s\"", Parameter); goto ERROR_EXIT; } if( wcscmp(L"RawDEMDataFile", Parameter) == 0 ) { ParseParameterString(m_strRawDEMDataFile, pConfigFile); } else if( wcscmp(L"MaterialMaskFile", Parameter) == 0 ) { ParseParameterString(m_strMtrlMaskFile, pConfigFile); } else if( wcsncmp(L"TileTexture", Parameter, _tcslen(L"TileTexture")) == 0 ) { int iTileNum = _tstoi(Parameter + _tcslen(L"TileTexture") ); if( iTileNum >= 0 && iTileNum < CEarthHemsiphere::NUM_TILE_TEXTURES ) { ParseParameterString(m_strTileTexPaths[iTileNum], pConfigFile); } } else if( wcsncmp(L"TileNormalMap", Parameter, _tcslen(L"TileNormalMap")) == 0 ) { int iTileNum = _tstoi(Parameter + _tcslen(L"TileNormalMap") ); if( iTileNum >= 0 && iTileNum < CEarthHemsiphere::NUM_TILE_TEXTURES ) { ParseParameterString(m_strNormalMapTexPaths[iTileNum], pConfigFile); } } else if( wcscmp(L"TexturingMode", Parameter) == 0 ) { TCHAR Value[128]; ParseParameterString(Value, _countof(Value), pConfigFile); if( wcscmp(L"HeightBased", Value) == 0 ) m_TerrainRenderParams.m_TexturingMode = SRenderingParams::TM_HEIGHT_BASED; else if( wcscmp(L"MaterialMask", Value) == 0 ) m_TerrainRenderParams.m_TexturingMode = SRenderingParams::TM_MATERIAL_MASK; else if( wcscmp(L"MaterialMaskNM", Value) == 0 ) m_TerrainRenderParams.m_TexturingMode = SRenderingParams::TM_MATERIAL_MASK_NM; else LOG_ERROR( L"Unknown texturing mode (%s)\n" L"Only the following modes are recognized:\n" L"HeightBased\n", Value); } else { TCHAR Value[128]; _ftscanf_s( pConfigFile, _T("%s"), Value, _countof(Value)); // Parameters if( wcscmp(L"ElevationSamplingInterval", Parameter) == 0 ) { m_TerrainRenderParams.m_TerrainAttribs.m_fElevationSamplingInterval = ParseParameterFloat( Value ); } else if(wcsncmp(L"TilingScale", Parameter, _tcslen(L"TilingScale")) == 0 ) { int iTileNum = _tstoi(Parameter + _tcslen(L"TilingScale") ); if( iTileNum == 0 ) { m_TerrainRenderParams.m_TerrainAttribs.m_fBaseMtrlTilingScale = ParseParameterFloat( Value ); } else if( iTileNum >= 1 && iTileNum < CEarthHemsiphere::NUM_TILE_TEXTURES ) { ((float*)&m_TerrainRenderParams.m_TerrainAttribs.m_f4TilingScale)[iTileNum-1] = ParseParameterFloat( Value ); } } else if( wcscmp(L"RingDimension", Parameter) == 0 ) { m_TerrainRenderParams.m_iRingDimension = ParseParameterInt( Value ); } else if( wcscmp(L"NumRings", Parameter) == 0 ) { m_TerrainRenderParams.m_iNumRings = ParseParameterInt( Value ); } else if( wcscmp(L"ColOffset", Parameter) == 0 ) { m_TerrainRenderParams.m_iColOffset = ParseParameterInt( Value ); } else if( wcscmp(L"RowOffset", Parameter) == 0 ) { m_TerrainRenderParams.m_iRowOffset = ParseParameterInt( Value ); } else if( wcscmp(L"AnimateSun", Parameter) == 0 ) { ParseParameterBool( Value, m_bAnimateSun ); } } } fclose(pConfigFile); return S_OK; ERROR_EXIT: fclose(pConfigFile); return E_FAIL; } ================================================ FILE: Terrain/DynamicQuadTreeNode.h ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #pragma once #include // Structure describing quad tree node location struct SQuadTreeNodeLocation { // Position in a tree int horzOrder; int vertOrder; int level; SQuadTreeNodeLocation(int h, int v, int l) : horzOrder(h) , vertOrder(v) , level(l) { assert(h < (1 << l)); assert(v < (1 << l)); } SQuadTreeNodeLocation() : horzOrder(0) , vertOrder(0) , level(0) {} // Gets location of a child inline friend SQuadTreeNodeLocation GetChildLocation(const SQuadTreeNodeLocation &parent, unsigned int siblingOrder) { return SQuadTreeNodeLocation(parent.horzOrder * 2 + (siblingOrder&1), parent.vertOrder * 2 + (siblingOrder>>1), parent.level + 1); } // Gets location of a parent inline friend SQuadTreeNodeLocation GetParentLocation(const SQuadTreeNodeLocation &node) { assert(node.level > 0); return SQuadTreeNodeLocation(node.horzOrder / 2, node.vertOrder / 2, node.level - 1); } }; // Base class for iterators traversing the quad tree class HierarchyIteratorBase { public: operator const SQuadTreeNodeLocation& () const { return m_current; } int Level() const { return m_current.level; } int Horz() const { return m_current.horzOrder; } int Vert() const { return m_current.vertOrder; } protected: SQuadTreeNodeLocation m_current; int m_currentLevelSize; }; // Iterator for recursively traversing the quad tree starting from the root up to the specified level class HierarchyIterator : public HierarchyIteratorBase { public: HierarchyIterator(int nLevels) : m_nLevels(nLevels) { m_currentLevelSize = 1; } bool IsValid() const { return m_current.level < m_nLevels; } void Next() { if( ++m_current.horzOrder == m_currentLevelSize ) { m_current.horzOrder = 0; if( ++m_current.vertOrder == m_currentLevelSize ) { m_current.vertOrder = 0; m_currentLevelSize = 1 << ++m_current.level; } } } private: int m_nLevels; }; // Iterator for recursively traversing the quad tree starting from the specified level up to the root class HierarchyReverseIterator : public HierarchyIteratorBase { public: HierarchyReverseIterator(int nLevels) { m_current.level = nLevels - 1; m_currentLevelSize = 1 << m_current.level; } bool IsValid() const { return m_current.level >= 0; } void Next() { if( ++m_current.horzOrder == m_currentLevelSize ) { m_current.horzOrder = 0; if( ++m_current.vertOrder == m_currentLevelSize ) { m_current.vertOrder = 0; m_currentLevelSize = 1 << --m_current.level; } } } }; // Template class for the node of a dynamic quad tree template class CDynamicQuadTreeNode { public: CDynamicQuadTreeNode() : m_pAncestor(NULL) { } NodeDataType &GetData(){return m_Data;} const NodeDataType &GetData()const{return m_Data;} CDynamicQuadTreeNode *GetAncestor() const { return m_pAncestor; } void GetDescendants(const CDynamicQuadTreeNode* &LBDescendant, const CDynamicQuadTreeNode* &RBDescendant, const CDynamicQuadTreeNode* <Descendant, const CDynamicQuadTreeNode* &RTDescendant) const { LBDescendant = m_pLBDescendant.get(); RBDescendant = m_pRBDescendant.get(); LTDescendant = m_pLTDescendant.get(); RTDescendant = m_pRTDescendant.get(); } void GetDescendants(CDynamicQuadTreeNode* &LBDescendant, CDynamicQuadTreeNode* &RBDescendant, CDynamicQuadTreeNode* <Descendant, CDynamicQuadTreeNode* &RTDescendant) { LBDescendant = m_pLBDescendant.get(); RBDescendant = m_pRBDescendant.get(); LTDescendant = m_pLTDescendant.get(); RTDescendant = m_pRTDescendant.get(); } typedef std::auto_ptr > AutoPtrType; // Attahes specified descendants to the tree void CreateDescendants(AutoPtrType pLBDescendant, AutoPtrType pRBDescendant, AutoPtrType pLTDescendant, AutoPtrType pRTDescendant); // Creates descendants UNATTACHED to the tree void CreateFloatingDescendants(AutoPtrType &pLBDescendant, AutoPtrType &pRBDescendant, AutoPtrType &pLTDescendant, AutoPtrType &pRTDescendant); // Destroys ALL descendants for the node void DestroyDescendants(); const SQuadTreeNodeLocation& GetPos() const { return m_pos; } void SetPos(const SQuadTreeNodeLocation& pos){ m_pos = pos; } private: CDynamicQuadTreeNode(CDynamicQuadTreeNode *pAncestor, int iSiblingOrder) : m_pAncestor(pAncestor), m_pos(GetChildLocation(pAncestor->m_pos, iSiblingOrder)) { } NodeDataType m_Data; std::auto_ptr< CDynamicQuadTreeNode > m_pLBDescendant; std::auto_ptr< CDynamicQuadTreeNode > m_pRBDescendant; std::auto_ptr< CDynamicQuadTreeNode > m_pLTDescendant; std::auto_ptr< CDynamicQuadTreeNode > m_pRTDescendant; CDynamicQuadTreeNode *m_pAncestor; SQuadTreeNodeLocation m_pos; }; template void CDynamicQuadTreeNode::CreateFloatingDescendants(AutoPtrType &pLBDescendant, AutoPtrType &pRBDescendant, AutoPtrType &pLTDescendant, AutoPtrType &pRTDescendant) { pLBDescendant.reset(new CDynamicQuadTreeNode(this, 0)); pRBDescendant.reset(new CDynamicQuadTreeNode(this, 1)); pLTDescendant.reset(new CDynamicQuadTreeNode(this, 2)); pRTDescendant.reset(new CDynamicQuadTreeNode(this, 3)); } template void CDynamicQuadTreeNode::CreateDescendants(AutoPtrType pLBDescendant, AutoPtrType pRBDescendant, AutoPtrType pLTDescendant, AutoPtrType pRTDescendant) { assert( !m_pLBDescendant.get() ); assert( !m_pRBDescendant.get() ); assert( !m_pLTDescendant.get() ); assert( !m_pRTDescendant.get() ); m_pLBDescendant = pLBDescendant; m_pRBDescendant = pRBDescendant; m_pLTDescendant = pLTDescendant; m_pRTDescendant = pRTDescendant; } template void CDynamicQuadTreeNode::DestroyDescendants() { if( m_pLBDescendant.get() )m_pLBDescendant->DestroyDescendants(); if( m_pRBDescendant.get() )m_pRBDescendant->DestroyDescendants(); if( m_pLTDescendant.get() )m_pLTDescendant->DestroyDescendants(); if( m_pRTDescendant.get() )m_pRTDescendant->DestroyDescendants(); m_pLBDescendant.reset(); m_pRBDescendant.reset(); m_pLTDescendant.reset(); m_pRTDescendant.reset(); } ================================================ FILE: Terrain/EarthHemisphere.cpp ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "EarthHemisphere.h" #include "Structures.fxh" #include "ElevationDataSource.h" #include "ShaderMacroHelper.h" void ExtractViewFrustumPlanesFromMatrix(const D3DXMATRIX &Matrix, SViewFrustum &ViewFrustum) { // For more details, see Gribb G., Hartmann K., "Fast Extraction of Viewing Frustum Planes from the // World-View-Projection Matrix" (the paper is available at // http://www2.ravensoft.com/users/ggribb/plane%20extraction.pdf) // Left clipping plane ViewFrustum.LeftPlane.Normal.x = Matrix._14 + Matrix._11; ViewFrustum.LeftPlane.Normal.y = Matrix._24 + Matrix._21; ViewFrustum.LeftPlane.Normal.z = Matrix._34 + Matrix._31; ViewFrustum.LeftPlane.Distance = Matrix._44 + Matrix._41; // Right clipping plane ViewFrustum.RightPlane.Normal.x = Matrix._14 - Matrix._11; ViewFrustum.RightPlane.Normal.y = Matrix._24 - Matrix._21; ViewFrustum.RightPlane.Normal.z = Matrix._34 - Matrix._31; ViewFrustum.RightPlane.Distance = Matrix._44 - Matrix._41; // Top clipping plane ViewFrustum.TopPlane.Normal.x = Matrix._14 - Matrix._12; ViewFrustum.TopPlane.Normal.y = Matrix._24 - Matrix._22; ViewFrustum.TopPlane.Normal.z = Matrix._34 - Matrix._32; ViewFrustum.TopPlane.Distance = Matrix._44 - Matrix._42; // Bottom clipping plane ViewFrustum.BottomPlane.Normal.x = Matrix._14 + Matrix._12; ViewFrustum.BottomPlane.Normal.y = Matrix._24 + Matrix._22; ViewFrustum.BottomPlane.Normal.z = Matrix._34 + Matrix._32; ViewFrustum.BottomPlane.Distance = Matrix._44 + Matrix._42; // Near clipping plane ViewFrustum.NearPlane.Normal.x = Matrix._13; ViewFrustum.NearPlane.Normal.y = Matrix._23; ViewFrustum.NearPlane.Normal.z = Matrix._33; ViewFrustum.NearPlane.Distance = Matrix._43; // Far clipping plane ViewFrustum.FarPlane.Normal.x = Matrix._14 - Matrix._13; ViewFrustum.FarPlane.Normal.y = Matrix._24 - Matrix._23; ViewFrustum.FarPlane.Normal.z = Matrix._34 - Matrix._33; ViewFrustum.FarPlane.Distance = Matrix._44 - Matrix._43; } bool IsBoxVisible(const SViewFrustum &ViewFrustum, const SBoundingBox &Box) { SPlane3D *pPlanes = (SPlane3D *)&ViewFrustum; // If bounding box is "behind" some plane, then it is invisible // Otherwise it is treated as visible for(int iViewFrustumPlane = 0; iViewFrustumPlane < 6; iViewFrustumPlane++) { SPlane3D *pCurrPlane = pPlanes + iViewFrustumPlane; D3DXVECTOR3 *pCurrNormal = &pCurrPlane->Normal; D3DXVECTOR3 MaxPoint; MaxPoint.x = (pCurrNormal->x > 0) ? Box.fMaxX : Box.fMinX; MaxPoint.y = (pCurrNormal->y > 0) ? Box.fMaxY : Box.fMinY; MaxPoint.z = (pCurrNormal->z > 0) ? Box.fMaxZ : Box.fMinZ; float DMax = D3DXVec3Dot( &MaxPoint, pCurrNormal ) + pCurrPlane->Distance; if( DMax < 0 ) return false; } return true; } struct SHemisphereVertex { D3DXVECTOR3 f3WorldPos; D3DXVECTOR2 f2MaskUV0; SHemisphereVertex() : f3WorldPos(0,0,0), f2MaskUV0(0,0){} }; enum QUAD_TRIANGULATION_TYPE { QUAD_TRIANG_TYPE_UNDEFINED = 0, // 01 11 // *------* // | .' | // | .' | // * -----* // 00 10 QUAD_TRIANG_TYPE_00_TO_11, // 01 11 // *------* // | '. | // | '. | // * -----* // 00 10 QUAD_TRIANG_TYPE_01_TO_10 }; template class CTriStrip { public: CTriStrip(std::vector &Indices, CIndexGenerator IndexGenerator): m_Indices(Indices), m_IndexGenerator(IndexGenerator), m_QuadTriangType(QUAD_TRIANG_TYPE_UNDEFINED) { } void AddStrip(int iBaseIndex, int iStartCol, int iStartRow, int iNumCols, int iNumRows, QUAD_TRIANGULATION_TYPE QuadTriangType) { assert( QuadTriangType == QUAD_TRIANG_TYPE_00_TO_11 || QuadTriangType == QUAD_TRIANG_TYPE_01_TO_10 ); int iFirstVertex = iBaseIndex + m_IndexGenerator(iStartCol, iStartRow + (QuadTriangType==QUAD_TRIANG_TYPE_00_TO_11 ? 1:0)); if(m_QuadTriangType != QUAD_TRIANG_TYPE_UNDEFINED) { // To move from one strip to another, we have to generate two degenerate triangles // by duplicating the last vertex in previous strip and the first vertex in new strip m_Indices.push_back( m_Indices.back() ); m_Indices.push_back( iFirstVertex ); } if(m_QuadTriangType != QUAD_TRIANG_TYPE_UNDEFINED && m_QuadTriangType != QuadTriangType || m_QuadTriangType == QUAD_TRIANG_TYPE_UNDEFINED && QuadTriangType == QUAD_TRIANG_TYPE_01_TO_10) { // If triangulation orientation changes, or if start strip orientation is 01 to 10, // we also have to add one additional vertex to preserve winding order m_Indices.push_back( iFirstVertex ); } m_QuadTriangType = QuadTriangType; for(int iRow = 0; iRow < iNumRows-1; ++iRow) { for(int iCol = 0; iCol < iNumCols; ++iCol) { int iV00 = iBaseIndex + m_IndexGenerator(iStartCol+iCol, iStartRow+iRow); int iV01 = iBaseIndex + m_IndexGenerator(iStartCol+iCol, iStartRow+iRow+1); if( m_QuadTriangType == QUAD_TRIANG_TYPE_01_TO_10 ) { if( iCol == 0 && iRow == 0) assert(iFirstVertex == iV00); // 01 11 // *------* // | '. | // | '. | // * -----* // 00 10 m_Indices.push_back(iV00); m_Indices.push_back(iV01); } else if( m_QuadTriangType == QUAD_TRIANG_TYPE_00_TO_11 ) { if( iCol == 0 && iRow == 0) assert(iFirstVertex == iV01); // 01 11 // *------* // | .' | // | .' | // * -----* // 00 10 m_Indices.push_back(iV01); m_Indices.push_back(iV00); } else { assert(false); } } if(iRow < iNumRows-2) { m_Indices.push_back( m_Indices.back() ); m_Indices.push_back( iBaseIndex + m_IndexGenerator(iStartCol, iStartRow+iRow+1 + (QuadTriangType==QUAD_TRIANG_TYPE_00_TO_11 ? 1:0)) ); } } } private: QUAD_TRIANGULATION_TYPE m_QuadTriangType; std::vector &m_Indices; CIndexGenerator m_IndexGenerator; }; class CStdIndexGenerator { public: CStdIndexGenerator(int iPitch):m_iPitch(iPitch){} UINT operator ()(int iCol, int iRow){return iCol + iRow*m_iPitch;} private: int m_iPitch; }; typedef CTriStrip StdTriStrip32; void ComputeVertexHeight(SHemisphereVertex &Vertex, class CElevationDataSource *pDataSource, float fSamplingStep, float fSampleScale) { D3DXVECTOR3 &f3PosWS = Vertex.f3WorldPos; float fCol = f3PosWS.x / fSamplingStep; float fRow = f3PosWS.z / fSamplingStep; float fDispl = pDataSource->GetInterpolatedHeight(fCol, fRow); int iColOffset, iRowOffset; pDataSource->GetOffsets(iColOffset, iRowOffset); Vertex.f2MaskUV0.x = (fCol + (float)iColOffset + 0.5f)/(float)pDataSource->GetNumCols(); Vertex.f2MaskUV0.y = (fRow + (float)iRowOffset + 0.5f)/(float)pDataSource->GetNumRows(); D3DXVECTOR3 f3SphereNormal; D3DXVec3Normalize(&f3SphereNormal, &f3PosWS); f3PosWS += f3SphereNormal * fDispl * fSampleScale; } class CRingMeshBuilder { public: CRingMeshBuilder(ID3D11Device *pDevice, const std::vector &VB, int iGridDimenion, std::vector &RingMeshes) : m_pDevice(pDevice), m_VB(VB), m_iGridDimenion(iGridDimenion), m_RingMeshes(RingMeshes){} void CreateMesh(int iBaseIndex, int iStartCol, int iStartRow, int iNumCols, int iNumRows, enum QUAD_TRIANGULATION_TYPE QuadTriangType) { m_RingMeshes.push_back( SRingSectorMesh() ); auto& CurrMesh = m_RingMeshes.back(); std::vector IB; StdTriStrip32 TriStrip( IB, CStdIndexGenerator(m_iGridDimenion) ); TriStrip.AddStrip(iBaseIndex, iStartCol, iStartRow, iNumCols, iNumRows, QuadTriangType); CurrMesh.uiNumIndices = (UINT)IB.size(); // Prepare buffer description D3D11_BUFFER_DESC IndexBufferDesc= { (UINT)(IB.size() * sizeof(IB[0])), D3D11_USAGE_IMMUTABLE, D3D11_BIND_INDEX_BUFFER, 0, //UINT CPUAccessFlags 0, //UINT MiscFlags; 0, //UINT StructureByteStride; }; D3D11_SUBRESOURCE_DATA IBInitData = {&IB[0], 0, 0}; // Create the buffer HRESULT hr; V( m_pDevice->CreateBuffer( &IndexBufferDesc, &IBInitData, &CurrMesh.pIndBuff) ); // Compute bounding box auto &BB = CurrMesh.BoundBox; BB.fMaxX =BB.fMaxY = BB.fMaxZ = -FLT_MAX; BB.fMinX =BB.fMinY = BB.fMinZ = +FLT_MAX; for(auto Ind = IB.begin(); Ind != IB.end(); ++Ind) { const auto &CurrVert = m_VB[*Ind].f3WorldPos; BB.fMinX = min(BB.fMinX, CurrVert.x); BB.fMinY = min(BB.fMinY, CurrVert.y); BB.fMinZ = min(BB.fMinZ, CurrVert.z); BB.fMaxX = max(BB.fMaxX, CurrVert.x); BB.fMaxY = max(BB.fMaxY, CurrVert.y); BB.fMaxZ = max(BB.fMaxZ, CurrVert.z); } } private: CComPtr m_pDevice; std::vector &m_RingMeshes; const std::vector &m_VB; const int m_iGridDimenion; }; void GenerateSphereGeometry(ID3D11Device *pDevice, const float fEarthRadius, int iGridDimension, const int iNumRings, class CElevationDataSource *pDataSource, float fSamplingStep, float fSampleScale, std::vector &VB, std::vector &StitchIB, std::vector &SphereMeshes) { if( (iGridDimension - 1) % 4 != 0 ) { assert(false); iGridDimension = SRenderingParams().m_iRingDimension; } const int iGridMidst = (iGridDimension-1)/2; const int iGridQuart = (iGridDimension-1)/4; const int iLargestGridScale = iGridDimension << (iNumRings-1); CRingMeshBuilder RingMeshBuilder(pDevice, VB, iGridDimension, SphereMeshes); int iStartRing = 0; VB.reserve( (iNumRings-iStartRing) * iGridDimension * iGridDimension ); for(int iRing = iStartRing; iRing < iNumRings; ++iRing) { int iCurrGridStart = (int)VB.size(); VB.resize(VB.size() + iGridDimension * iGridDimension); float fGridScale = 1.f / (float)(1<<(iNumRings-1 - iRing)); // Fill vertex buffer for(int iRow = 0; iRow < iGridDimension; ++iRow) for(int iCol = 0; iCol < iGridDimension; ++iCol) { auto &CurrVert = VB[iCurrGridStart + iCol + iRow*iGridDimension]; auto &f3Pos = CurrVert.f3WorldPos; f3Pos.x = static_cast(iCol) / static_cast(iGridDimension-1); f3Pos.z = static_cast(iRow) / static_cast(iGridDimension-1); f3Pos.x = f3Pos.x*2 - 1; f3Pos.z = f3Pos.z*2 - 1; f3Pos.y = 0; float fDirectionScale = 1; if( f3Pos.x != 0 || f3Pos.z != 0 ) { float fDX = abs(f3Pos.x); float fDZ = abs(f3Pos.z); float fMaxD = max(fDX, fDZ); float fMinD = min(fDX, fDZ); float fTan = fMinD/fMaxD; fDirectionScale = 1 / sqrt(1 + fTan*fTan); } f3Pos.x *= fDirectionScale*fGridScale; f3Pos.z *= fDirectionScale*fGridScale; f3Pos.y = sqrt( max(0, 1 - (f3Pos.x*f3Pos.x + f3Pos.z*f3Pos.z)) ); f3Pos.x *= fEarthRadius; f3Pos.z *= fEarthRadius; f3Pos.y *= fEarthRadius; ComputeVertexHeight(CurrVert, pDataSource, fSamplingStep, fSampleScale); f3Pos.y -= fEarthRadius; } // Align vertices on the outer boundary if( iRing < iNumRings-1 ) { for(int i=1; i < iGridDimension-1; i+=2) { // Top & bottom boundaries for(int iRow=0; iRow < iGridDimension; iRow += iGridDimension-1) { const auto &V0 = VB[iCurrGridStart + i - 1 + iRow*iGridDimension].f3WorldPos; auto &V1 = VB[iCurrGridStart + i + 0 + iRow*iGridDimension].f3WorldPos; const auto &V2 = VB[iCurrGridStart + i + 1 + iRow*iGridDimension].f3WorldPos; V1 = (V0+V2)/2.f; } // Left & right boundaries for(int iCol=0; iCol < iGridDimension; iCol += iGridDimension-1) { const auto &V0 = VB[iCurrGridStart + iCol + (i - 1)*iGridDimension].f3WorldPos; auto &V1 = VB[iCurrGridStart + iCol + (i + 0)*iGridDimension].f3WorldPos; const auto &V2 = VB[iCurrGridStart + iCol + (i + 1)*iGridDimension].f3WorldPos; V1 = (V0+V2)/2.f; } } // Add triangles stitching this ring with the next one int iNextGridStart = (int)VB.size(); assert( iNextGridStart == iCurrGridStart + iGridDimension*iGridDimension); // Bottom boundary for(int iCol=0; iCol < iGridDimension-1; iCol += 2) { StitchIB.push_back(iNextGridStart + (iGridQuart + iCol/2) + iGridQuart * iGridDimension); StitchIB.push_back(iCurrGridStart + (iCol+1) + 0 * iGridDimension); StitchIB.push_back(iCurrGridStart + (iCol+0) + 0 * iGridDimension); StitchIB.push_back(iNextGridStart + (iGridQuart + iCol/2) + iGridQuart * iGridDimension); StitchIB.push_back(iCurrGridStart + (iCol+2) + 0 * iGridDimension); StitchIB.push_back(iCurrGridStart + (iCol+1) + 0 * iGridDimension); StitchIB.push_back(iNextGridStart + (iGridQuart + iCol/2) + iGridQuart * iGridDimension); StitchIB.push_back(iNextGridStart + (iGridQuart + iCol/2+1) + iGridQuart * iGridDimension); StitchIB.push_back(iCurrGridStart + (iCol+2) + 0 * iGridDimension); } // Top boundary for(int iCol=0; iCol < iGridDimension-1; iCol += 2) { StitchIB.push_back(iCurrGridStart + (iCol+0) + (iGridDimension-1) * iGridDimension); StitchIB.push_back(iCurrGridStart + (iCol+1) + (iGridDimension-1) * iGridDimension); StitchIB.push_back(iNextGridStart + (iGridQuart + iCol/2) + iGridQuart* 3 * iGridDimension); StitchIB.push_back(iCurrGridStart + (iCol+1) + (iGridDimension-1) * iGridDimension); StitchIB.push_back(iCurrGridStart + (iCol+2) + (iGridDimension-1) * iGridDimension); StitchIB.push_back(iNextGridStart + (iGridQuart + iCol/2) + iGridQuart* 3 * iGridDimension); StitchIB.push_back(iCurrGridStart + (iCol+2) + (iGridDimension-1) * iGridDimension); StitchIB.push_back(iNextGridStart + (iGridQuart + iCol/2 + 1) + iGridQuart* 3 * iGridDimension); StitchIB.push_back(iNextGridStart + (iGridQuart + iCol/2) + iGridQuart* 3 * iGridDimension); } // Left boundary for(int iRow=0; iRow < iGridDimension-1; iRow += 2) { StitchIB.push_back(iNextGridStart + iGridQuart + (iGridQuart+ iRow/2) * iGridDimension); StitchIB.push_back(iCurrGridStart + 0 + (iRow+0) * iGridDimension); StitchIB.push_back(iCurrGridStart + 0 + (iRow+1) * iGridDimension); StitchIB.push_back(iNextGridStart + iGridQuart + (iGridQuart+ iRow/2) * iGridDimension); StitchIB.push_back(iCurrGridStart + 0 + (iRow+1) * iGridDimension); StitchIB.push_back(iCurrGridStart + 0 + (iRow+2) * iGridDimension); StitchIB.push_back(iNextGridStart + iGridQuart + (iGridQuart + iRow/2 + 1) * iGridDimension); StitchIB.push_back(iNextGridStart + iGridQuart + (iGridQuart + iRow/2) * iGridDimension); StitchIB.push_back(iCurrGridStart + 0 + (iRow+2) * iGridDimension); } // Right boundary for(int iRow=0; iRow < iGridDimension-1; iRow += 2) { StitchIB.push_back(iCurrGridStart + (iGridDimension-1) + (iRow+1) * iGridDimension); StitchIB.push_back(iCurrGridStart + (iGridDimension-1) + (iRow+0) * iGridDimension); StitchIB.push_back(iNextGridStart + iGridQuart*3 + (iGridQuart+ iRow/2) * iGridDimension); StitchIB.push_back(iCurrGridStart + (iGridDimension-1) + (iRow+2) * iGridDimension); StitchIB.push_back(iCurrGridStart + (iGridDimension-1) + (iRow+1) * iGridDimension); StitchIB.push_back(iNextGridStart + iGridQuart*3 + (iGridQuart+ iRow/2) * iGridDimension); StitchIB.push_back(iCurrGridStart + (iGridDimension-1) + (iRow+2) * iGridDimension); StitchIB.push_back(iNextGridStart + iGridQuart*3 + (iGridQuart+ iRow/2) * iGridDimension); StitchIB.push_back(iNextGridStart + iGridQuart*3 + (iGridQuart+ iRow/2 + 1) * iGridDimension); } } // Generate indices for the current ring if( iRing == 0 ) { RingMeshBuilder.CreateMesh( iCurrGridStart, 0, 0, iGridMidst+1, iGridMidst+1, QUAD_TRIANG_TYPE_00_TO_11); RingMeshBuilder.CreateMesh( iCurrGridStart, iGridMidst, 0, iGridMidst+1, iGridMidst+1, QUAD_TRIANG_TYPE_01_TO_10); RingMeshBuilder.CreateMesh( iCurrGridStart, 0, iGridMidst, iGridMidst+1, iGridMidst+1, QUAD_TRIANG_TYPE_01_TO_10); RingMeshBuilder.CreateMesh( iCurrGridStart, iGridMidst, iGridMidst, iGridMidst+1, iGridMidst+1, QUAD_TRIANG_TYPE_00_TO_11); } else { RingMeshBuilder.CreateMesh( iCurrGridStart, 0, 0, iGridQuart+1, iGridQuart+1, QUAD_TRIANG_TYPE_00_TO_11); RingMeshBuilder.CreateMesh( iCurrGridStart, iGridQuart, 0, iGridQuart+1, iGridQuart+1, QUAD_TRIANG_TYPE_00_TO_11); RingMeshBuilder.CreateMesh( iCurrGridStart, iGridMidst, 0, iGridQuart+1, iGridQuart+1, QUAD_TRIANG_TYPE_01_TO_10); RingMeshBuilder.CreateMesh( iCurrGridStart, iGridQuart*3, 0, iGridQuart+1, iGridQuart+1, QUAD_TRIANG_TYPE_01_TO_10); RingMeshBuilder.CreateMesh( iCurrGridStart, 0, iGridQuart, iGridQuart+1, iGridQuart+1, QUAD_TRIANG_TYPE_00_TO_11); RingMeshBuilder.CreateMesh( iCurrGridStart, 0, iGridMidst, iGridQuart+1, iGridQuart+1, QUAD_TRIANG_TYPE_01_TO_10); RingMeshBuilder.CreateMesh( iCurrGridStart, iGridQuart*3, iGridQuart, iGridQuart+1, iGridQuart+1, QUAD_TRIANG_TYPE_01_TO_10); RingMeshBuilder.CreateMesh( iCurrGridStart, iGridQuart*3, iGridMidst, iGridQuart+1, iGridQuart+1, QUAD_TRIANG_TYPE_00_TO_11); RingMeshBuilder.CreateMesh( iCurrGridStart, 0, iGridQuart*3, iGridQuart+1, iGridQuart+1, QUAD_TRIANG_TYPE_01_TO_10); RingMeshBuilder.CreateMesh( iCurrGridStart, iGridQuart, iGridQuart*3, iGridQuart+1, iGridQuart+1, QUAD_TRIANG_TYPE_01_TO_10); RingMeshBuilder.CreateMesh( iCurrGridStart, iGridMidst, iGridQuart*3, iGridQuart+1, iGridQuart+1, QUAD_TRIANG_TYPE_00_TO_11); RingMeshBuilder.CreateMesh( iCurrGridStart, iGridQuart*3, iGridQuart*3, iGridQuart+1, iGridQuart+1, QUAD_TRIANG_TYPE_00_TO_11); } } // We do not need per-vertex normals as we use normal map to shade terrain // Sphere tangent vertex are computed in the shader #if 0 // Compute normals const D3DXVECTOR3 *pV0 = nullptr; const D3DXVECTOR3 *pV1 = &VB[ IB[0] ].f3WorldPos; const D3DXVECTOR3 *pV2 = &VB[ IB[1] ].f3WorldPos; float fSign = +1; for(UINT Ind=2; Ind < m_uiIndicesInIndBuff; ++Ind) { fSign = -fSign; pV0 = pV1; pV1 = pV2; pV2 = &VB[ IB[Ind] ].f3WorldPos; D3DXVECTOR3 Rib0 = *pV0 - *pV1; D3DXVECTOR3 Rib1 = *pV1 - *pV2; D3DXVECTOR3 TriN; D3DXVec3Cross(&TriN, &Rib0, &Rib1); float fLength = D3DXVec3Length(&TriN); if( fLength > 0.1 ) { TriN /= fLength*fSign; for(int i=-2; i <= 0; ++i) VB[ IB[Ind+i] ].f3Normal += TriN; } } for(auto VBIt=VB.begin(); VBIt != VB.end(); ++VBIt) { float fLength = D3DXVec3Length(&VBIt->f3Normal); if( fLength > 1 ) VBIt->f3Normal /= fLength; } // Adjust normals on boundaries for(int iRing = iStartRing; iRing < iNumRings-1; ++iRing) { int iCurrGridStart = (iRing-iStartRing) * iGridDimension*iGridDimension; int iNextGridStart = (iRing-iStartRing+1) * iGridDimension*iGridDimension; for(int i=0; i < iGridDimension; i+=2) { for(int Bnd=0; Bnd < 2; ++Bnd) { const int CurrGridOffsets[] = {0, iGridDimension-1}; const int NextGridPffsets[] = {iGridQuart, iGridQuart*3}; // Left and right boundaries { auto &CurrGridN = VB[iCurrGridStart + CurrGridOffsets[Bnd] + i*iGridDimension].f3Normal; auto &NextGridN = VB[iNextGridStart + NextGridPffsets[Bnd] + (iGridQuart+i/2)*iGridDimension].f3Normal; auto NewN = CurrGridN + NextGridN; D3DXVec3Normalize(&NewN, &NewN); CurrGridN = NextGridN = NewN; if( i > 1 ) { auto &PrevCurrGridN = VB[iCurrGridStart + CurrGridOffsets[Bnd] + (i-2)*iGridDimension].f3Normal; auto MiddleN = PrevCurrGridN + NewN; D3DXVec3Normalize( &VB[iCurrGridStart + CurrGridOffsets[Bnd] + (i-1)*iGridDimension].f3Normal, &MiddleN); } } // Bottom and top boundaries { auto &CurrGridN = VB[iCurrGridStart + i + CurrGridOffsets[Bnd]*iGridDimension].f3Normal; auto &NextGridN = VB[iNextGridStart + (iGridQuart+i/2) + NextGridPffsets[Bnd]*iGridDimension].f3Normal; auto NewN = CurrGridN + NextGridN; D3DXVec3Normalize(&NewN, &NewN); CurrGridN = NextGridN = NewN; if( i > 1 ) { auto &PrevCurrGridN = VB[iCurrGridStart + (i-2) + CurrGridOffsets[Bnd]*iGridDimension].f3Normal; auto MiddleN = PrevCurrGridN + NewN; D3DXVec3Normalize( &VB[iCurrGridStart + (i-1) + CurrGridOffsets[Bnd]*iGridDimension].f3Normal, &MiddleN); } } } } } #endif } HRESULT CEarthHemsiphere::CreateRenderStates(ID3D11Device* pd3dDevice) { HRESULT hr; // Create depth stencil state D3D11_DEPTH_STENCIL_DESC DisableDepthTestDSDesc; ZeroMemory(&DisableDepthTestDSDesc, sizeof(DisableDepthTestDSDesc)); DisableDepthTestDSDesc.DepthEnable = FALSE; DisableDepthTestDSDesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ZERO; V( pd3dDevice->CreateDepthStencilState( &DisableDepthTestDSDesc, &m_pDisableDepthTestDS) ); D3D11_DEPTH_STENCIL_DESC EnableDepthTestDSDesc; ZeroMemory(&EnableDepthTestDSDesc, sizeof(EnableDepthTestDSDesc)); EnableDepthTestDSDesc.DepthEnable = TRUE; EnableDepthTestDSDesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL; EnableDepthTestDSDesc.DepthFunc = D3D11_COMPARISON_GREATER; V( pd3dDevice->CreateDepthStencilState( &EnableDepthTestDSDesc, &m_pEnableDepthTestDS) ); // Create default blend state D3D11_BLEND_DESC DefaultBlendStateDesc; ZeroMemory(&DefaultBlendStateDesc, sizeof(DefaultBlendStateDesc)); DefaultBlendStateDesc.IndependentBlendEnable = FALSE; for(int i=0; i< _countof(DefaultBlendStateDesc.RenderTarget); i++) DefaultBlendStateDesc.RenderTarget[i].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; V( pd3dDevice->CreateBlendState( &DefaultBlendStateDesc, &m_pDefaultBS) ); // Create rasterizer state for solid fill mode D3D11_RASTERIZER_DESC RSSolidFill = { D3D11_FILL_SOLID, D3D11_CULL_BACK, TRUE, //BOOL FrontCounterClockwise; 0,// INT DepthBias; 0,// FLOAT DepthBiasClamp; 0,// FLOAT SlopeScaledDepthBias; TRUE,//BOOL DepthClipEnable; FALSE,//BOOL ScissorEnable; FALSE,//BOOL MultisampleEnable; FALSE,//BOOL AntialiasedLineEnable; }; hr = pd3dDevice->CreateRasterizerState( &RSSolidFill, &m_pRSSolidFill ); D3D11_RASTERIZER_DESC RSWireframeFill = RSSolidFill; RSWireframeFill.FillMode = D3D11_FILL_WIREFRAME; hr = pd3dDevice->CreateRasterizerState( &RSWireframeFill, &m_pRSWireframeFill ); D3D11_RASTERIZER_DESC SolidFillNoCullRSDesc; ZeroMemory(&SolidFillNoCullRSDesc, sizeof(SolidFillNoCullRSDesc)); SolidFillNoCullRSDesc.FillMode = D3D11_FILL_SOLID; SolidFillNoCullRSDesc.CullMode = D3D11_CULL_NONE; V( pd3dDevice->CreateRasterizerState( &SolidFillNoCullRSDesc, &m_pRSSolidFillNoCull) ); D3D11_RASTERIZER_DESC ZOnlyPassRSDesc = RSSolidFill; // Disable depth clipping ZOnlyPassRSDesc.DepthClipEnable = FALSE; // Do not use slope-scaled depth bias because this results in light leaking // through terrain! //ZOnlyPassRSDesc.DepthBias = -1; //ZOnlyPassRSDesc.SlopeScaledDepthBias = -4; //ZOnlyPassRSDesc.DepthBiasClamp = -1e-6; ZOnlyPassRSDesc.FrontCounterClockwise = FALSE; V( pd3dDevice->CreateRasterizerState( &ZOnlyPassRSDesc, &m_pRSZOnlyPass) ); D3D11_SAMPLER_DESC SamLinearMirrorDesc = { D3D11_FILTER_MIN_MAG_MIP_LINEAR, D3D11_TEXTURE_ADDRESS_MIRROR, D3D11_TEXTURE_ADDRESS_MIRROR, D3D11_TEXTURE_ADDRESS_MIRROR, 0, //FLOAT MipLODBias; 0, //UINT MaxAnisotropy; D3D11_COMPARISON_NEVER, // D3D11_COMPARISON_FUNC ComparisonFunc; {0.f, 0.f, 0.f, 0.f}, //FLOAT BorderColor[ 4 ]; -FLT_MAX, //FLOAT MinLOD; +FLT_MAX //FLOAT MaxLOD; }; V( pd3dDevice->CreateSamplerState( &SamLinearMirrorDesc, &m_psamLinearMirror) ); D3D11_SAMPLER_DESC SamPointClamp = { D3D11_FILTER_MIN_MAG_MIP_POINT, D3D11_TEXTURE_ADDRESS_CLAMP, D3D11_TEXTURE_ADDRESS_CLAMP, D3D11_TEXTURE_ADDRESS_CLAMP, 0, //FLOAT MipLODBias; 0, //UINT MaxAnisotropy; D3D11_COMPARISON_NEVER, // D3D11_COMPARISON_FUNC ComparisonFunc; {0.f, 0.f, 0.f, 0.f}, //FLOAT BorderColor[ 4 ]; -FLT_MAX, //FLOAT MinLOD; +FLT_MAX //FLOAT MaxLOD; }; V( pd3dDevice->CreateSamplerState( &SamPointClamp, &m_psamPointClamp) ); D3D11_SAMPLER_DESC SamLinearClamp = { D3D11_FILTER_MIN_MAG_MIP_LINEAR, D3D11_TEXTURE_ADDRESS_CLAMP, D3D11_TEXTURE_ADDRESS_CLAMP, D3D11_TEXTURE_ADDRESS_CLAMP, 0, //FLOAT MipLODBias; 0, //UINT MaxAnisotropy; D3D11_COMPARISON_NEVER, // D3D11_COMPARISON_FUNC ComparisonFunc; {0.f, 0.f, 0.f, 0.f}, //FLOAT BorderColor[ 4 ]; -FLT_MAX, //FLOAT MinLOD; +FLT_MAX //FLOAT MaxLOD; }; V( pd3dDevice->CreateSamplerState( &SamLinearClamp, &m_psamLinearClamp) ); D3D11_SAMPLER_DESC SamComparison = { D3D11_FILTER_COMPARISON_MIN_MAG_MIP_LINEAR, D3D11_TEXTURE_ADDRESS_CLAMP, D3D11_TEXTURE_ADDRESS_CLAMP, D3D11_TEXTURE_ADDRESS_CLAMP, 0, //FLOAT MipLODBias; 0, //UINT MaxAnisotropy; D3D11_COMPARISON_GREATER, // D3D11_COMPARISON_FUNC ComparisonFunc; {0.f, 0.f, 0.f, 0.f}, //FLOAT BorderColor[ 4 ]; -FLT_MAX, //FLOAT MinLOD; +FLT_MAX //FLOAT MaxLOD; }; V( pd3dDevice->CreateSamplerState( &SamComparison, &m_psamComaprison) ); D3D11_SAMPLER_DESC SamLinearWrapDesc = { D3D11_FILTER_MIN_MAG_MIP_LINEAR, D3D11_TEXTURE_ADDRESS_WRAP, D3D11_TEXTURE_ADDRESS_WRAP, D3D11_TEXTURE_ADDRESS_WRAP, 0, //FLOAT MipLODBias; 0, //UINT MaxAnisotropy; D3D11_COMPARISON_NEVER, // D3D11_COMPARISON_FUNC ComparisonFunc; {0.f, 0.f, 0.f, 0.f}, //FLOAT BorderColor[ 4 ]; -FLT_MAX, //FLOAT MinLOD; +FLT_MAX //FLOAT MaxLOD; }; V( pd3dDevice->CreateSamplerState( &SamLinearWrapDesc, &m_psamLinearWrap) ); return S_OK; } void CEarthHemsiphere::RenderNormalMap(ID3D11Device* pd3dDevice, ID3D11DeviceContext* pd3dImmediateContext, const UINT16 *pHeightMap, size_t HeightMapPitch, int iHeightMapDim) { HRESULT hr; D3D11_TEXTURE2D_DESC HeightMapDesc = { iHeightMapDim, iHeightMapDim, 1, 1, DXGI_FORMAT_R16_UNORM, {1,0}, D3D11_USAGE_IMMUTABLE, D3D11_BIND_SHADER_RESOURCE, 0, 0 }; while( (iHeightMapDim >> HeightMapDesc.MipLevels) > 1 ) ++HeightMapDesc.MipLevels; std::vector CoarseMipLevels; CoarseMipLevels.resize( iHeightMapDim/2 * iHeightMapDim ); std::vector InitData(HeightMapDesc.MipLevels); InitData[0].pSysMem = pHeightMap; InitData[0].SysMemPitch = (UINT)HeightMapPitch*sizeof(pHeightMap[0]); const UINT16 *pFinerMipLevel = pHeightMap; UINT16 *pCurrMipLevel = &CoarseMipLevels[0]; size_t FinerMipPitch = HeightMapPitch; size_t CurrMipPitch = iHeightMapDim/2; for(UINT uiMipLevel = 1; uiMipLevel < HeightMapDesc.MipLevels; ++uiMipLevel) { auto MipWidth = HeightMapDesc.Width >> uiMipLevel; auto MipHeight = HeightMapDesc.Height >> uiMipLevel; for(UINT uiRow=0; uiRow < MipHeight; ++uiRow) for(UINT uiCol=0; uiCol < MipWidth; ++uiCol) { int iAverageHeight = 0; for(int i=0; i<2; ++i) for(int j=0; j<2; ++j) iAverageHeight += pFinerMipLevel[ (uiCol*2+i) + (uiRow*2+j)*FinerMipPitch]; pCurrMipLevel[uiCol + uiRow*CurrMipPitch] = (UINT16)(iAverageHeight>>2); } InitData[uiMipLevel].pSysMem = pCurrMipLevel; InitData[uiMipLevel].SysMemPitch = (UINT)CurrMipPitch*sizeof(*pCurrMipLevel); pFinerMipLevel = pCurrMipLevel; FinerMipPitch = CurrMipPitch; pCurrMipLevel += MipHeight*CurrMipPitch; CurrMipPitch = iHeightMapDim/2; } CComPtr ptex2DHeightMap; pd3dDevice->CreateTexture2D(&HeightMapDesc, &InitData[0], &ptex2DHeightMap); CComPtr ptex2DHeightMapSRV; pd3dDevice->CreateShaderResourceView(ptex2DHeightMap, nullptr, &ptex2DHeightMapSRV); D3D11_TEXTURE2D_DESC NormalMapDesc = HeightMapDesc; NormalMapDesc.Format = DXGI_FORMAT_R8G8_SNORM; NormalMapDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET; NormalMapDesc.Usage = D3D11_USAGE_DEFAULT; CComPtr ptex2DNormalMap; pd3dDevice->CreateTexture2D(&NormalMapDesc, &InitData[0], &ptex2DNormalMap); pd3dDevice->CreateShaderResourceView(ptex2DNormalMap, nullptr, &m_ptex2DNormalMapSRV); CComPtr pOrigRTV; CComPtr pOrigDSV; pd3dImmediateContext->OMGetRenderTargets( 1, &pOrigRTV, &pOrigDSV ); D3D11_VIEWPORT OrigViewPort; UINT iNumOldViewports = 1; pd3dImmediateContext->RSGetViewports(&iNumOldViewports, &OrigViewPort); CRenderTechnique RenderNormalMapTech; RenderNormalMapTech.SetDeviceAndContext(pd3dDevice, pd3dImmediateContext); V( RenderNormalMapTech.CreateVGPShadersFromFile( L"fx\\Terrain.fx", "GenerateScreenSizeQuadVS", NULL, "GenerateNormalMapPS", NULL ) ); RenderNormalMapTech.SetDS( m_pDisableDepthTestDS ); RenderNormalMapTech.SetRS( m_pRSSolidFillNoCull ); RenderNormalMapTech.SetBS( m_pDefaultBS ); D3D11_VIEWPORT NewViewPort; NewViewPort.TopLeftX = 0; NewViewPort.TopLeftY = 0; NewViewPort.MinDepth = 0; NewViewPort.MaxDepth = 1; D3D11_BUFFER_DESC CBDesc = { sizeof(SNMGenerationAttribs), D3D11_USAGE_DYNAMIC, D3D11_BIND_CONSTANT_BUFFER, D3D11_CPU_ACCESS_WRITE, //UINT CPUAccessFlags 0, //UINT MiscFlags; 0, //UINT StructureByteStride; }; CComPtr pcbNMGenerationAttribs; V( pd3dDevice->CreateBuffer( &CBDesc, NULL, &pcbNMGenerationAttribs) ); pd3dImmediateContext->PSSetConstantBuffers(0, 1, &pcbNMGenerationAttribs.p); pd3dImmediateContext->PSSetShaderResources(0, 1, &ptex2DHeightMapSRV.p); pd3dImmediateContext->PSSetSamplers(0, 1, &m_psamPointClamp.p); for(UINT uiMipLevel = 0; uiMipLevel < NormalMapDesc.MipLevels; ++uiMipLevel) { D3D11_RENDER_TARGET_VIEW_DESC RTVDesc; RTVDesc.Format = NormalMapDesc.Format; RTVDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D; RTVDesc.Texture2D.MipSlice = uiMipLevel; CComPtr ptex2DNormalMapRTV; pd3dDevice->CreateRenderTargetView(ptex2DNormalMap, &RTVDesc, &ptex2DNormalMapRTV); NewViewPort.Width = (float)(NormalMapDesc.Width >> uiMipLevel); NewViewPort.Height = (float)(NormalMapDesc.Height >> uiMipLevel); pd3dImmediateContext->RSSetViewports(1, &NewViewPort); pd3dImmediateContext->OMSetRenderTargets(1, &ptex2DNormalMapRTV.p, NULL); SNMGenerationAttribs NMGenerationAttribs; NMGenerationAttribs.m_fElevationScale = m_Params.m_TerrainAttribs.m_fElevationScale; NMGenerationAttribs.m_fSampleSpacingInterval = m_Params.m_TerrainAttribs.m_fElevationSamplingInterval; NMGenerationAttribs.m_fMIPLevel = (float)uiMipLevel; UpdateConstantBuffer(pd3dImmediateContext, pcbNMGenerationAttribs, &NMGenerationAttribs, sizeof(NMGenerationAttribs)); RenderNormalMapTech.Apply(); pd3dImmediateContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); pd3dImmediateContext->Draw(4,0); } pd3dImmediateContext->RSSetViewports(iNumOldViewports, &OrigViewPort); pd3dImmediateContext->OMSetRenderTargets( 1, &pOrigRTV.p, pOrigDSV ); } HRESULT CEarthHemsiphere::OnD3D11CreateDevice( class CElevationDataSource *pDataSource, const SRenderingParams &Params, ID3D11Device* pd3dDevice, ID3D11DeviceContext* pd3dImmediateContext, LPCTSTR HeightMapPath, LPCTSTR MaterialMaskPath, LPCTSTR *TileTexturePath, LPCTSTR *TileNormalMapPath) { HRESULT hr; m_Params = Params; const UINT16 *pHeightMap; size_t HeightMapPitch; pDataSource->GetDataPtr(pHeightMap, HeightMapPitch); int iHeightMapDim = pDataSource->GetNumCols(); assert(iHeightMapDim == pDataSource->GetNumRows() ); std::vector VB; std::vector StitchIB; GenerateSphereGeometry(pd3dDevice, SAirScatteringAttribs().fEarthRadius, m_Params.m_iRingDimension, m_Params.m_iNumRings, pDataSource, m_Params.m_TerrainAttribs.m_fElevationSamplingInterval, m_Params.m_TerrainAttribs.m_fElevationScale, VB, StitchIB, m_SphereMeshes); D3D11_BUFFER_DESC VBDesc = { (UINT)(VB.size() * sizeof(VB[0])), D3D11_USAGE_IMMUTABLE, D3D11_BIND_VERTEX_BUFFER, 0, //UINT CPUAccessFlags 0, //UINT MiscFlags; 0, //UINT StructureByteStride; }; D3D11_SUBRESOURCE_DATA VBInitData = {&VB[0], 0, 0}; hr = pd3dDevice->CreateBuffer(&VBDesc, &VBInitData , &m_pVertBuff); CHECK_HR_RET(hr, _T("Failed to create the Earth heimsphere vertex buffer") ) m_uiNumStitchIndices = (UINT)StitchIB.size(); D3D11_BUFFER_DESC StitchIndexBufferDesc= { (UINT)(m_uiNumStitchIndices * sizeof(StitchIB[0])), D3D11_USAGE_IMMUTABLE, D3D11_BIND_INDEX_BUFFER, 0, //UINT CPUAccessFlags 0, //UINT MiscFlags; 0, //UINT StructureByteStride; }; D3D11_SUBRESOURCE_DATA IBInitData = {&StitchIB[0], 0, 0}; // Create the buffer V( pd3dDevice->CreateBuffer( &StitchIndexBufferDesc, &IBInitData, &m_pStitchIndBuff) ); V( CreateRenderStates(pd3dDevice) ); m_RenderEarthHemisphereZOnlyTech.SetDeviceAndContext(pd3dDevice, pd3dImmediateContext); V( m_RenderEarthHemisphereZOnlyTech.CreateVGPShadersFromFile( L"fx\\Terrain.fx", "HemisphereZOnlyVS", NULL, NULL, NULL ) ); m_RenderEarthHemisphereZOnlyTech.SetDS( m_pEnableDepthTestDS ); m_RenderEarthHemisphereZOnlyTech.SetRS( m_pRSZOnlyPass ); m_RenderEarthHemisphereZOnlyTech.SetBS( m_pDefaultBS ); RenderNormalMap(pd3dDevice, pd3dImmediateContext, pHeightMap, HeightMapPitch, iHeightMapDim); D3DX11CreateShaderResourceViewFromFile(pd3dDevice, MaterialMaskPath, nullptr, nullptr, &m_ptex2DMtrlMaskSRV, nullptr); // Load tiles for(int iTileTex = 0; iTileTex < (int)NUM_TILE_TEXTURES; iTileTex++) { V( D3DX11CreateShaderResourceViewFromFile(pd3dDevice, TileTexturePath[iTileTex], NULL, NULL, &m_ptex2DTilesSRV[iTileTex], NULL) ); D3DX11_IMAGE_LOAD_INFO LoadInfo; memset( &LoadInfo, 0, sizeof(LoadInfo)); D3DX11_IMAGE_INFO NMFileInfo; D3DX11GetImageInfoFromFile(TileNormalMapPath[iTileTex],NULL,&NMFileInfo,NULL); LoadInfo.Width = NMFileInfo.Width; LoadInfo.Height = NMFileInfo.Height; LoadInfo.Depth = NMFileInfo.Depth; LoadInfo.MipLevels = (int)( log( (double)max(NMFileInfo.Width, NMFileInfo.Height)) / log(2.0) ); LoadInfo.BindFlags = D3D11_BIND_SHADER_RESOURCE; LoadInfo.Usage = D3D11_USAGE_IMMUTABLE; LoadInfo.Format = DXGI_FORMAT_R8G8B8A8_UNORM; LoadInfo.MipFilter = D3DX11_DEFAULT; LoadInfo.Filter = D3DX11_DEFAULT; V( D3DX11CreateShaderResourceViewFromFile(pd3dDevice, TileNormalMapPath[iTileTex], &LoadInfo, NULL, &m_ptex2DTilNormalMapsSRV[iTileTex], NULL) ); } D3D11_BUFFER_DESC CBDesc = { 0, D3D11_USAGE_DYNAMIC, D3D11_BIND_CONSTANT_BUFFER, D3D11_CPU_ACCESS_WRITE, //UINT CPUAccessFlags 0, //UINT MiscFlags; 0, //UINT StructureByteStride; }; CBDesc.ByteWidth = sizeof(STerrainAttribs); V( pd3dDevice->CreateBuffer( &CBDesc, NULL, &m_pcbTerrainAttribs) ); CBDesc.ByteWidth = sizeof(SCameraAttribs); V( pd3dDevice->CreateBuffer( &CBDesc, NULL, &m_pcbCameraAttribs) ); return S_OK; } void CEarthHemsiphere::OnD3D11DestroyDevice() { m_pStitchIndBuff.Release(); m_SphereMeshes.clear(); m_pVertBuff.Release(); m_pInputLayout.Release(); m_RenderEarthHemisphereTech.Release(); m_RenderEarthHemisphereZOnlyTech.Release(); m_psamPointClamp.Release(); m_psamLinearMirror.Release(); m_psamLinearWrap.Release(); m_psamLinearClamp.Release(); m_psamComaprison.Release(); m_pEnableDepthTestDS.Release(); m_pDisableDepthTestDS.Release(); m_pDefaultBS.Release(); m_pRSSolidFill.Release(); m_pRSSolidFillNoCull.Release(); m_pRSZOnlyPass.Release(); m_pRSWireframeFill.Release(); m_ptex2DNormalMapSRV.Release(); m_pcbTerrainAttribs.Release(); m_pcbCameraAttribs.Release(); for(int iTileTex = 0; iTileTex < NUM_TILE_TEXTURES; iTileTex++) { m_ptex2DTilesSRV[iTileTex].Release(); m_ptex2DTilNormalMapsSRV[iTileTex].Release(); } } void CEarthHemsiphere::Render(ID3D11DeviceContext* pd3dImmediateContext, const D3DXVECTOR3 &vCameraPosition, const D3DXMATRIX &CameraViewProjMatrix, ID3D11Buffer *pcbLightAttribs, ID3D11Buffer *pcMediaScatteringParams, ID3D11ShaderResourceView *pShadowMapSRV, ID3D11ShaderResourceView *pPrecomputedNetDensitySRV, ID3D11ShaderResourceView *pAmbientSkylightSRV, bool bZOnlyPass) { if( GetAsyncKeyState(VK_F9) ) { m_RenderEarthHemisphereTech.Release(); } if( !m_RenderEarthHemisphereTech.IsValid() ) { HRESULT hr; CD3DShaderMacroHelper Macros; Macros.AddShaderMacro("TEXTURING_MODE", m_Params.m_TexturingMode); Macros.AddShaderMacro("NUM_TILE_TEXTURES", NUM_TILE_TEXTURES); Macros.AddShaderMacro("NUM_SHADOW_CASCADES", m_Params.m_iNumShadowCascades); Macros.AddShaderMacro("BEST_CASCADE_SEARCH", m_Params.m_bBestCascadeSearch ? true : false); Macros.AddShaderMacro("SMOOTH_SHADOWS", m_Params.m_bSmoothShadows ? true : false); Macros.Finalize(); CComPtr pDevice; pd3dImmediateContext->GetDevice(&pDevice); m_RenderEarthHemisphereTech.SetDeviceAndContext(pDevice, pd3dImmediateContext); V( m_RenderEarthHemisphereTech.CreateVGPShadersFromFile( L"fx\\Terrain.fx", "HemisphereVS", NULL, "HemispherePS", Macros ) ); m_RenderEarthHemisphereTech.SetDS( m_pEnableDepthTestDS ); m_RenderEarthHemisphereTech.SetRS( m_pRSSolidFill ); m_RenderEarthHemisphereTech.SetBS( m_pDefaultBS ); if( !m_pInputLayout ) { // Create vertex input layout for bounding box buffer const D3D11_INPUT_ELEMENT_DESC layout[] = { { "WORLD_POS", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0*4, D3D11_INPUT_PER_VERTEX_DATA, 0 }, { "MASK0_UV", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 3*4, D3D11_INPUT_PER_VERTEX_DATA, 0 }, }; auto pVSByteCode = m_RenderEarthHemisphereTech.GetVSByteCode(); V( pDevice->CreateInputLayout( layout, ARRAYSIZE( layout ), pVSByteCode->GetBufferPointer(), pVSByteCode->GetBufferSize(), &m_pInputLayout ) ); } } SViewFrustum ViewFrustum; ExtractViewFrustumPlanesFromMatrix(CameraViewProjMatrix, ViewFrustum); UpdateConstantBuffer(pd3dImmediateContext, m_pcbTerrainAttribs, &m_Params.m_TerrainAttribs, sizeof(m_Params.m_TerrainAttribs)); SCameraAttribs CameraAttribs; D3DXMatrixTranspose(&CameraAttribs.WorldViewProjT, &CameraViewProjMatrix); CameraAttribs.f4CameraPos = D3DXVECTOR4(vCameraPosition.x, vCameraPosition.y, vCameraPosition.z, 0); UpdateConstantBuffer(pd3dImmediateContext, m_pcbCameraAttribs, &CameraAttribs, sizeof(CameraAttribs)); ID3D11Buffer *pCBs[] = { m_pcbTerrainAttribs, m_pcbCameraAttribs, pcbLightAttribs, pcMediaScatteringParams }; pd3dImmediateContext->VSSetConstantBuffers(0, _countof(pCBs), pCBs); pd3dImmediateContext->PSSetConstantBuffers(0, _countof(pCBs), pCBs); ID3D11ShaderResourceView *pSRVs[3 + 2*NUM_TILE_TEXTURES] = { m_ptex2DNormalMapSRV, m_ptex2DMtrlMaskSRV, pShadowMapSRV }; for(int iTileTex = 0; iTileTex < NUM_TILE_TEXTURES; iTileTex++) { pSRVs[3+iTileTex] = m_ptex2DTilesSRV[iTileTex]; pSRVs[3+NUM_TILE_TEXTURES+iTileTex] = m_ptex2DTilNormalMapsSRV[iTileTex]; } pd3dImmediateContext->PSSetShaderResources(1, _countof(pSRVs), pSRVs); pSRVs[0] = pPrecomputedNetDensitySRV; pSRVs[1] = pAmbientSkylightSRV; pd3dImmediateContext->VSSetShaderResources(0, 2, pSRVs); ID3D11SamplerState *pSamplers[] = {m_psamLinearMirror, m_psamLinearWrap, m_psamComaprison, m_psamLinearClamp}; pd3dImmediateContext->VSSetSamplers(0, _countof(pSamplers), pSamplers); pd3dImmediateContext->PSSetSamplers(0, _countof(pSamplers), pSamplers); UINT offset[1] = { 0 }; UINT stride[1] = { sizeof(SHemisphereVertex) }; ID3D11Buffer* const ppBuffers[1] = { m_pVertBuff }; pd3dImmediateContext->IASetVertexBuffers( 0, 1, ppBuffers, stride, offset ); pd3dImmediateContext->IASetInputLayout( m_pInputLayout ); pd3dImmediateContext->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP ); if( bZOnlyPass ) m_RenderEarthHemisphereZOnlyTech.Apply(); else m_RenderEarthHemisphereTech.Apply(); for(auto MeshIt = m_SphereMeshes.begin(); MeshIt != m_SphereMeshes.end(); ++MeshIt) { if(IsBoxVisible(ViewFrustum, MeshIt->BoundBox)) { pd3dImmediateContext->IASetIndexBuffer( MeshIt->pIndBuff, DXGI_FORMAT_R32_UINT, 0); pd3dImmediateContext->DrawIndexed(MeshIt->uiNumIndices, 0, 0); } } pd3dImmediateContext->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST ); pd3dImmediateContext->IASetIndexBuffer( m_pStitchIndBuff, DXGI_FORMAT_R32_UINT, 0); pd3dImmediateContext->DrawIndexed(m_uiNumStitchIndices, 0, 0); UnbindPSResources(pd3dImmediateContext); } void CEarthHemsiphere::UpdateParams(const SRenderingParams &NewParams) { if( m_Params.m_iNumShadowCascades != NewParams.m_iNumShadowCascades || m_Params.m_bBestCascadeSearch != NewParams.m_bBestCascadeSearch || m_Params.m_bSmoothShadows != NewParams.m_bSmoothShadows ) { m_RenderEarthHemisphereTech.Release(); } m_Params = NewParams; } ================================================ FILE: Terrain/EarthHemisphere.h ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #pragma once #include "RenderTechnique.h" #include "TerrainStructs.fxh" struct SBoundingBox { float fMinX, fMaxX, fMinY, fMaxY, fMinZ, fMaxZ; }; // Structure describing a plane struct SPlane3D { D3DXVECTOR3 Normal; float Distance; //Distance from the coordinate system origin to the plane along normal direction }; #pragma pack(1) struct SViewFrustum { SPlane3D LeftPlane, RightPlane, BottomPlane, TopPlane, NearPlane, FarPlane; }; #pragma pack() // Extract view frustum planes from the world-view-projection matrix void ExtractViewFrustumPlanesFromMatrix(const D3DXMATRIX &Matrix, SViewFrustum &ViewFrustum); // Tests if bounding box is visible by the camera bool IsBoxVisible(const SViewFrustum &ViewFrustum, const SBoundingBox &Box); // Structure describing terrain rendering parameters struct SRenderingParams { STerrainAttribs m_TerrainAttribs; enum TEXTURING_MODE { TM_HEIGHT_BASED = 0, TM_MATERIAL_MASK = 1, TM_MATERIAL_MASK_NM = 2 }; // Patch rendering params TEXTURING_MODE m_TexturingMode; int m_iRingDimension; int m_iNumRings; int m_iNumShadowCascades; BOOL m_bBestCascadeSearch; BOOL m_bSmoothShadows; int m_iColOffset, m_iRowOffset; SRenderingParams() : m_TexturingMode(TM_MATERIAL_MASK), m_iRingDimension(65), m_iNumRings(15), m_iNumShadowCascades(4), m_bBestCascadeSearch(TRUE), m_bSmoothShadows(TRUE), m_iColOffset(0), m_iRowOffset(0) {} }; struct SRingSectorMesh { CComPtr pIndBuff; UINT uiNumIndices; SBoundingBox BoundBox; SRingSectorMesh() : uiNumIndices(0){} }; // This class renders the adaptive model using DX11 API class CEarthHemsiphere { public: CEarthHemsiphere(void) : m_uiNumStitchIndices(0){} // Renders the model void Render(ID3D11DeviceContext* pd3dImmediateContext, const D3DXVECTOR3 &vCameraPosition, const D3DXMATRIX &CameraViewProjMatrix, ID3D11Buffer *pcbLightAttribs, ID3D11Buffer *pcMediaScatteringParams, ID3D11ShaderResourceView *pShadowMapSRV, ID3D11ShaderResourceView *pPrecomputedNetDensitySRV, ID3D11ShaderResourceView *pAmbientSkylightSRV, bool bZOnlyPass); // Creates Direct3D11 device resources HRESULT OnD3D11CreateDevice( class CElevationDataSource *pDataSource, const SRenderingParams &Params, ID3D11Device* pd3dDevice, ID3D11DeviceContext* pd3dImmediateContext, LPCTSTR HeightMapPath, LPCTSTR MaterialMaskPath, LPCTSTR *TileTexturePath, LPCTSTR *TileNormalMapPath ); // Releases Direct3D11 device resources void OnD3D11DestroyDevice( ); enum {NUM_TILE_TEXTURES = 1 + 4};// One base material + 4 masked materials void UpdateParams(const SRenderingParams &NewParams); private: HRESULT CreateRenderStates(ID3D11Device* pd3dDevice); void RenderNormalMap(ID3D11Device* pd3dDevice, ID3D11DeviceContext* pd3dImmediateContext, const UINT16 *pHeightMap, size_t HeightMapPitch, int iHeightMapDim); SRenderingParams m_Params; CRenderTechnique m_RenderEarthHemisphereTech; CRenderTechnique m_RenderEarthHemisphereZOnlyTech; CComPtr m_pVertBuff; CComPtr m_pInputLayout; CComPtr m_ptex2DNormalMapSRV, m_ptex2DMtrlMaskSRV; CComPtr m_pEnableDepthTestDS; CComPtr m_pDisableDepthTestDS; CComPtr m_pDefaultBS; CComPtr m_pRSSolidFill, m_pRSSolidFillNoCull, m_pRSZOnlyPass, m_pRSWireframeFill; CComPtr m_psamPointClamp, m_psamLinearMirror, m_psamLinearWrap, m_psamComaprison, m_psamLinearClamp; CComPtr m_pcbTerrainAttribs; CComPtr m_pcbCameraAttribs; CComPtr m_ptex2DTilesSRV[NUM_TILE_TEXTURES]; CComPtr m_ptex2DTilNormalMapsSRV[NUM_TILE_TEXTURES]; std::vector m_SphereMeshes; CComPtr m_pStitchIndBuff; UINT m_uiNumStitchIndices; private: CEarthHemsiphere(const CEarthHemsiphere&); CEarthHemsiphere& operator = (const CEarthHemsiphere&); }; ================================================ FILE: Terrain/ElevationDataSource.cpp ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "ElevationDataSource.h" #include "DynamicQuadTreeNode.h" #include #include #include #pragma comment(lib, "WindowsCodecs.lib") // Creates data source from the specified raw data file CElevationDataSource::CElevationDataSource(LPCTSTR strSrcDemFile) : m_iPatchSize(128), m_iNumLevels(0), m_iColOffset(0), m_iRowOffset(0) { HRESULT hr; V( CoInitialize(NULL) ); // Create components to read 16-bit png data CComPtr pFactory; hr = pFactory.CoCreateInstance(CLSID_WICImagingFactory); if( FAILED(hr) ) throw std::exception("Failed to create WICImagingFactory"); CComPtr pInputStream; hr = pFactory->CreateStream(&pInputStream); if( FAILED(hr) ) throw std::exception("Failed to create WICStream"); hr = pInputStream->InitializeFromFilename(strSrcDemFile, GENERIC_READ); if( FAILED(hr) ) throw std::exception("Failed to initialize WICStream from file"); CComPtr pDecoder; hr = pFactory->CreateDecoderFromStream( pInputStream, 0, // vendor WICDecodeMetadataCacheOnDemand, &pDecoder); if( FAILED(hr) ) throw std::exception("Failed to Create decoder from stream"); //GUID ContainerFormat; //pDecoder->GetContainerFormat(&ContainerFormat); //if( ContainerFormat == GUID_ContainerFormatPng ) // printf("Container Format: PNG\n"); //else if( ContainerFormat == GUID_ContainerFormatTiff ) // printf("Container Format: TIFF\n"); UINT frameCount = 0; hr = pDecoder->GetFrameCount(&frameCount); //printf("Frame count %d\n", frameCount); assert( frameCount == 1 ); CComPtr pTheFrame; pDecoder->GetFrame(0, &pTheFrame); UINT width = 0; UINT height = 0; pTheFrame->GetSize(&width, &height); // Calculate minimal number of columns and rows // in the form 2^n+1 that encompass the data m_iNumCols = 1; m_iNumRows = 1; while( m_iNumCols+1 < width || m_iNumRows+1 < height) { m_iNumCols *= 2; m_iNumRows *= 2; } m_iNumLevels = 1; while( (m_iPatchSize << (m_iNumLevels-1)) < (int)m_iNumCols || (m_iPatchSize << (m_iNumLevels-1)) < (int)m_iNumRows ) m_iNumLevels++; m_iNumCols++; m_iNumRows++; GUID pixelFormat = { 0 }; pTheFrame->GetPixelFormat(&pixelFormat); if( pixelFormat != GUID_WICPixelFormat16bppGray ) { assert(false); throw std::exception("expected 16 bit format"); } // Load the data m_TheHeightMap.resize( m_iNumCols * m_iNumRows ); WICRect SrcRect; SrcRect.X = 0; SrcRect.Y = 0; SrcRect.Height = height; SrcRect.Width = width; pTheFrame->CopyPixels( &SrcRect, (UINT)m_iNumCols*2, //UINT stride (UINT)m_TheHeightMap.size()*2, //UINT bufferSize (BYTE*)&m_TheHeightMap[0]); // Duplicate the last row and column for(UINT iRow = 0; iRow < height; iRow++) for(UINT iCol = width; iCol < m_iNumCols; iCol++) m_TheHeightMap[iCol + iRow * m_iNumCols] = m_TheHeightMap[(width-1) + iRow * m_iNumCols]; for(UINT iCol = 0; iCol < m_iNumCols; iCol++) for(UINT iRow = height; iRow < m_iNumRows; iRow++) m_TheHeightMap[iCol + iRow * m_iNumCols] = m_TheHeightMap[iCol + (height-1) * m_iNumCols]; pTheFrame.Release(); pFactory.Release(); pDecoder.Release(); pInputStream.Release(); CoUninitialize(); m_MinMaxElevation.Resize(m_iNumLevels); // Calcualte min/max elevations CalculateMinMaxElevations(); } CElevationDataSource::~CElevationDataSource(void) { } UINT16 CElevationDataSource :: GetGlobalMinElevation()const { return m_MinMaxElevation[SQuadTreeNodeLocation()].first; } UINT16 CElevationDataSource :: GetGlobalMaxElevation()const { return m_MinMaxElevation[SQuadTreeNodeLocation()].second; } int MirrorCoord(int iCoord, int iDim) { iCoord = abs(iCoord); int iPeriod = iCoord / iDim; iCoord = iCoord % iDim; if( iPeriod & 0x01 ) { iCoord = (iDim-1) - iCoord; } return iCoord; } float CElevationDataSource::GetInterpolatedHeight(float fCol, float fRow, int iStep)const { float fCol0 = floor(fCol); float fRow0 = floor(fRow); int iCol0 = static_cast(fCol0); int iRow0 = static_cast(fRow0); iCol0 = (iCol0/iStep)*iStep; iRow0 = (iRow0/iStep)*iStep; float fHWeight = (fCol - (float)iCol0) / (float)iStep; float fVWeight = (fRow - (float)iRow0) / (float)iStep; iCol0 += m_iColOffset; iRow0 += m_iRowOffset; //if( iCol0 < 0 || iCol0 >= (int)m_iNumCols || iRow0 < 0 || iRow0 >= (int)m_iNumRows ) // return -FLT_MAX; int iCol1 = iCol0+iStep;//min(iCol0+iStep, (int)m_iNumCols-1); int iRow1 = iRow0+iStep;//min(iRow0+iStep, (int)m_iNumRows-1); iCol0 = MirrorCoord(iCol0, m_iNumCols); iCol1 = MirrorCoord(iCol1, m_iNumCols); iRow0 = MirrorCoord(iRow0, m_iNumRows); iRow1 = MirrorCoord(iRow1, m_iNumRows); UINT16 H00 = m_TheHeightMap[iCol0 + iRow0 * m_iNumCols]; UINT16 H10 = m_TheHeightMap[iCol1 + iRow0 * m_iNumCols]; UINT16 H01 = m_TheHeightMap[iCol0 + iRow1 * m_iNumCols]; UINT16 H11 = m_TheHeightMap[iCol1 + iRow1 * m_iNumCols]; float fInterpolatedHeight = (H00 * (1 - fHWeight) + H10 * fHWeight) * (1-fVWeight) + (H01 * (1 - fHWeight) + H11 * fHWeight) * fVWeight; return fInterpolatedHeight; } D3DXVECTOR3 CElevationDataSource::ComputeSurfaceNormal(float fCol, float fRow, float fSampleSpacing, float fHeightScale, int iStep)const { float Height1 = GetInterpolatedHeight(fCol + (float)iStep, fRow, iStep); float Height2 = GetInterpolatedHeight(fCol - (float)iStep, fRow, iStep); float Height3 = GetInterpolatedHeight(fCol, fRow + (float)iStep, iStep); float Height4 = GetInterpolatedHeight(fCol, fRow - (float)iStep, iStep); D3DXVECTOR3 Grad; Grad.x = Height2 - Height1; Grad.y = Height4 - Height3; Grad.z = (float)iStep * fSampleSpacing * 2.f; Grad.x *= fHeightScale; Grad.y *= fHeightScale; D3DXVECTOR3 Normal; D3DXVec3Normalize(&Normal, &Grad); return Normal; } void CElevationDataSource::RecomputePatchMinMaxElevations(const SQuadTreeNodeLocation &pos) { if( pos.level == m_iNumLevels-1 ) { std::pair &CurrPatchMinMaxElev = m_MinMaxElevation[SQuadTreeNodeLocation(pos.horzOrder, pos.vertOrder, pos.level)]; int iStartCol = pos.horzOrder*m_iPatchSize; int iStartRow = pos.vertOrder*m_iPatchSize; CurrPatchMinMaxElev.first = CurrPatchMinMaxElev.second = m_TheHeightMap[iStartCol + iStartRow*m_iNumCols]; for(int iRow = iStartRow; iRow <= iStartRow + m_iPatchSize; iRow++) for(int iCol = iStartCol; iCol <= iStartCol + m_iPatchSize; iCol++) { UINT16 CurrElev = m_TheHeightMap[iCol + iRow*m_iNumCols]; CurrPatchMinMaxElev.first = min(CurrPatchMinMaxElev.first, CurrElev); CurrPatchMinMaxElev.second = max(CurrPatchMinMaxElev.second, CurrElev); } } else { std::pair &CurrPatchMinMaxElev = m_MinMaxElevation[pos]; std::pair &LBChildMinMaxElev = m_MinMaxElevation[GetChildLocation(pos, 0)]; std::pair &RBChildMinMaxElev = m_MinMaxElevation[GetChildLocation(pos, 1)]; std::pair <ChildMinMaxElev = m_MinMaxElevation[GetChildLocation(pos, 2)]; std::pair &RTChildMinMaxElev = m_MinMaxElevation[GetChildLocation(pos, 3)]; CurrPatchMinMaxElev.first = min( LBChildMinMaxElev.first, RBChildMinMaxElev.first ); CurrPatchMinMaxElev.first = min( CurrPatchMinMaxElev.first, LTChildMinMaxElev.first ); CurrPatchMinMaxElev.first = min( CurrPatchMinMaxElev.first, RTChildMinMaxElev.first ); CurrPatchMinMaxElev.second = max( LBChildMinMaxElev.second, RBChildMinMaxElev.second); CurrPatchMinMaxElev.second = max( CurrPatchMinMaxElev.second, LTChildMinMaxElev.second ); CurrPatchMinMaxElev.second = max( CurrPatchMinMaxElev.second, RTChildMinMaxElev.second ); } } // Calculates min/max elevations for the hierarchy void CElevationDataSource :: CalculateMinMaxElevations() { // Calculate min/max elevations starting from the finest level for( HierarchyReverseIterator it(m_iNumLevels); it.IsValid(); it.Next() ) { RecomputePatchMinMaxElevations(it); } } void CElevationDataSource::GetDataPtr(const UINT16* &pDataPtr, size_t &Pitch) { pDataPtr = &m_TheHeightMap[0]; Pitch = m_iNumCols; } ================================================ FILE: Terrain/ElevationDataSource.h ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #pragma once #include #include "HierarchyArray.h" #include "DynamicQuadTreeNode.h" // Class implementing elevation data source class CElevationDataSource { public: // Creates data source from the specified raw data file CElevationDataSource(LPCTSTR strSrcDemFile); virtual ~CElevationDataSource(void); void GetDataPtr(const UINT16* &pDataPtr, size_t &Pitch); // Returns minimal height of the whole terrain UINT16 GetGlobalMinElevation()const; // Returns maximal height of the whole terrain UINT16 GetGlobalMaxElevation()const; void RecomputePatchMinMaxElevations(const SQuadTreeNodeLocation &pos); void SetOffsets(int iColOffset, int iRowOffset){m_iColOffset = iColOffset; m_iRowOffset = iRowOffset;} void GetOffsets(int &iColOffset, int &iRowOffset)const{iColOffset = m_iColOffset; iRowOffset = m_iRowOffset;} float GetInterpolatedHeight(float fCol, float fRow, int iStep = 1)const; D3DXVECTOR3 ComputeSurfaceNormal(float fCol, float fRow, float fSampleSpacing, float fHeightScale, int iStep = 1)const; unsigned int GetNumCols()const{return m_iNumCols;} unsigned int GetNumRows()const{return m_iNumRows;} private: CElevationDataSource(); // Calculates min/max elevations for all patches in the tree void CalculateMinMaxElevations(); // Hierarchy array storing minimal and maximal heights for quad tree nodes HierarchyArray< std::pair > m_MinMaxElevation; int m_iNumLevels; int m_iPatchSize; int m_iColOffset, m_iRowOffset; // The whole terrain height map std::vector m_TheHeightMap; unsigned int m_iNumCols, m_iNumRows; }; ================================================ FILE: Terrain/Errors.h ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #pragma once #define LOG_ERROR(ErrorMsg, ...)\ { \ TCHAR FormattedErrorMsg[256]; \ _stprintf_s(FormattedErrorMsg, sizeof(FormattedErrorMsg)/sizeof(FormattedErrorMsg[0]), ErrorMsg, __VA_ARGS__ ); \ TCHAR FullErrorMsg[1024]; \ _stprintf_s(FullErrorMsg, sizeof(FullErrorMsg)/sizeof(FullErrorMsg[0]), _T("The following error occured in the %s function() (%s, line %d):\n%s"), _T(__FUNCTION__), _T(__FILE__), __LINE__, FormattedErrorMsg); \ MessageBox(NULL, FullErrorMsg, _T("Error"), MB_ICONERROR|MB_OK ); \ } #define CHECK_HR(Result, ErrorMsg, ...)\ if( FAILED(Result) ) \ LOG_ERROR(ErrorMsg, __VA_ARGS__); #define CHECK_HR_RET(Result, ErrorMsg, ...)\ if( FAILED(Result) ) \ { \ LOG_ERROR(ErrorMsg, __VA_ARGS__); \ return Result; \ } ================================================ FILE: Terrain/HierarchyArray.h ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #pragma once #include #include "DynamicQuadTreeNode.h" // Template class implementing hierarchy array, which is a quad tree indexed by // quad tree node location template class HierarchyArray { public: T& operator [] (const SQuadTreeNodeLocation &at) { return m_data[at.level][at.horzOrder + (at.vertOrder << at.level)]; } const T& operator [] (const SQuadTreeNodeLocation &at) const { return m_data[at.level][at.horzOrder + (at.vertOrder << at.level)]; } void Resize(size_t numLevelsInHierarchy) { m_data.resize(numLevelsInHierarchy); if( numLevelsInHierarchy ) { for(size_t level = numLevelsInHierarchy; level--; ) { size_t numElementsInLevel = 1 << level; m_data[level].resize(numElementsInLevel*numElementsInLevel); } } } bool Empty() const { return m_data.empty(); } private: std::vector > m_data; }; // end of file ================================================ FILE: Terrain/stdafx.cpp ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #include "stdafx.h" // end of file ================================================ FILE: fx/Common.fxh ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #include "Structures.fxh" #define FLT_MAX 3.402823466e+38f #define RGB_TO_LUMINANCE float3(0.212671, 0.715160, 0.072169) // Using static definitions instead of constant buffer variables is // more efficient because the compiler is able to optimize the code // more aggressively #ifndef NUM_EPIPOLAR_SLICES # define NUM_EPIPOLAR_SLICES 1024 #endif #ifndef MAX_SAMPLES_IN_SLICE # define MAX_SAMPLES_IN_SLICE 512 #endif #ifndef SCREEN_RESLOUTION # define SCREEN_RESLOUTION float2(1024,768) #endif #define MIN_MAX_DATA_FORMAT float2 #ifndef CASCADE_PROCESSING_MODE # define CASCADE_PROCESSING_MODE CASCADE_PROCESSING_MODE_SINGLE_PASS #endif #ifndef USE_COMBINED_MIN_MAX_TEXTURE # define USE_COMBINED_MIN_MAX_TEXTURE 1 #endif #ifndef EXTINCTION_EVAL_MODE # define EXTINCTION_EVAL_MODE EXTINCTION_EVAL_MODE_EPIPOLAR #endif #ifndef AUTO_EXPOSURE # define AUTO_EXPOSURE 1 #endif cbuffer cbPostProcessingAttribs : register( b0 ) { SPostProcessingAttribs g_PPAttribs; }; cbuffer cbParticipatingMediaScatteringParams : register( b1 ) { SAirScatteringAttribs g_MediaParams; } // Frame parameters cbuffer cbCameraAttribs : register( b2 ) { SCameraAttribs g_CameraAttribs; } cbuffer cbLightParams : register( b3 ) { SLightAttribs g_LightAttribs; } cbuffer cbMiscDynamicParams : register( b4 ) { SMiscDynamicParams g_MiscParams; } SamplerState samLinearClamp : register( s0 ) { Filter = MIN_MAG_MIP_LINEAR; AddressU = Clamp; AddressV = Clamp; }; Texture2D g_tex2DDepthBuffer : register( t0 ); Texture2D g_tex2DCamSpaceZ : register( t0 ); Texture2D g_tex2DSliceEndPoints : register( t4 ); Texture2D g_tex2DCoordinates : register( t1 ); Texture2D g_tex2DEpipolarCamSpaceZ : register( t2 ); Texture2D g_tex2DInterpolationSource : register( t6 ); Texture2DArray g_tex2DLightSpaceDepthMap : register( t3 ); Texture2D g_tex2DSliceUVDirAndOrigin : register( t6 ); Texture2D g_tex2DMinMaxLightSpaceDepth : register( t4 ); Texture2D g_tex2DInitialInsctrIrradiance: register( t5 ); Texture2D g_tex2DColorBuffer : register( t1 ); Texture2D g_tex2DScatteredColor : register( t3 ); Texture2D g_tex2DOccludedNetDensityToAtmTop : register( t5 ); Texture2D g_tex2DEpipolarExtinction : register( t6 ); Texture3D g_tex3DSingleSctrLUT : register( t7 ); Texture3D g_tex3DHighOrderSctrLUT : register( t8 ); Texture3D g_tex3DMultipleSctrLUT : register( t9 ); Texture2D g_tex2DSphereRandomSampling : register( t1 ); Texture3D g_tex3DPreviousSctrOrder : register( t0 ); Texture3D g_tex3DPointwiseSctrRadiance : register( t0 ); Texture2D g_tex2DAverageLuminance : register( t10 ); Texture2D g_tex2DLowResLuminance : register( t0 ); float3 ProjSpaceXYZToWorldSpace(in float3 f3PosPS) { // We need to compute depth before applying view-proj inverse matrix float fDepth = g_CameraAttribs.mProj[2][2] + g_CameraAttribs.mProj[3][2] / f3PosPS.z; float4 ReconstructedPosWS = mul( float4(f3PosPS.xy,fDepth,1), g_CameraAttribs.mViewProjInv ); ReconstructedPosWS /= ReconstructedPosWS.w; return ReconstructedPosWS.xyz; } void GetRaySphereIntersection(in float3 f3RayOrigin, in float3 f3RayDirection, in float3 f3SphereCenter, in float fSphereRadius, out float2 f2Intersections) { // http://wiki.cgsociety.org/index.php/Ray_Sphere_Intersection f3RayOrigin -= f3SphereCenter; float A = dot(f3RayDirection, f3RayDirection); float B = 2 * dot(f3RayOrigin, f3RayDirection); float C = dot(f3RayOrigin,f3RayOrigin) - fSphereRadius*fSphereRadius; float D = B*B - 4*A*C; // If discriminant is negative, there are no real roots hence the ray misses the // sphere if( D<0 ) { f2Intersections = -1; } else { D = sqrt(D); f2Intersections = float2(-B - D, -B + D) / (2*A); // A must be positive here!! } } void GetRaySphereIntersection2(in float3 f3RayOrigin, in float3 f3RayDirection, in float3 f3SphereCenter, in float2 f2SphereRadius, out float4 f4Intersections) { // http://wiki.cgsociety.org/index.php/Ray_Sphere_Intersection f3RayOrigin -= f3SphereCenter; float A = dot(f3RayDirection, f3RayDirection); float B = 2 * dot(f3RayOrigin, f3RayDirection); float2 C = dot(f3RayOrigin,f3RayOrigin) - f2SphereRadius*f2SphereRadius; float2 D = B*B - 4*A*C; // If discriminant is negative, there are no real roots hence the ray misses the // sphere float2 f2RealRootMask = (D.xy >= 0); D = sqrt( max(D,0) ); f4Intersections = f2RealRootMask.xxyy * float4(-B - D.x, -B + D.x, -B - D.y, -B + D.y) / (2*A) + (1-f2RealRootMask.xxyy) * float4(-1,-1,-1,-1); } float GetAverageSceneLuminance() { #if AUTO_EXPOSURE float fAveLogLum = g_tex2DAverageLuminance.Load( int3(0,0,0) ); #else float fAveLogLum = 0.1; #endif fAveLogLum = max(0.05, fAveLogLum); // Average luminance is an approximation to the key of the scene return fAveLogLum; } ================================================ FILE: fx/LightScattering.fx ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #include "Common.fxh" #ifndef OPTIMIZE_SAMPLE_LOCATIONS # define OPTIMIZE_SAMPLE_LOCATIONS 1 #endif #ifndef CORRECT_INSCATTERING_AT_DEPTH_BREAKS # define CORRECT_INSCATTERING_AT_DEPTH_BREAKS 0 #endif //#define SHADOW_MAP_DEPTH_BIAS 1e-4 #ifndef TRAPEZOIDAL_INTEGRATION # define TRAPEZOIDAL_INTEGRATION 1 #endif #ifndef EARTH_RADIUS # define EARTH_RADIUS 6360000.f #endif #ifndef ATM_TOP_HEIGHT # define ATM_TOP_HEIGHT 80000.f #endif #ifndef ATM_TOP_RADIUS # define ATM_TOP_RADIUS (EARTH_RADIUS+ATM_TOP_HEIGHT) #endif #ifndef PARTICLE_SCALE_HEIGHT # define PARTICLE_SCALE_HEIGHT float2(7994.f, 1200.f) #endif #ifndef ENABLE_LIGHT_SHAFTS # define ENABLE_LIGHT_SHAFTS 1 #endif #ifndef IS_32BIT_MIN_MAX_MAP # define IS_32BIT_MIN_MAX_MAP 0 #endif #ifndef SINGLE_SCATTERING_MODE # define SINGLE_SCATTERING_MODE SINGLE_SCTR_MODE_LUT #endif #ifndef MULTIPLE_SCATTERING_MODE # define MULTIPLE_SCATTERING_MODE MULTIPLE_SCTR_MODE_OCCLUDED #endif #ifndef PRECOMPUTED_SCTR_LUT_DIM # define PRECOMPUTED_SCTR_LUT_DIM float4(32,128,32,16) #endif #ifndef NUM_RANDOM_SPHERE_SAMPLES # define NUM_RANDOM_SPHERE_SAMPLES 128 #endif #ifndef PERFORM_TONE_MAPPING # define PERFORM_TONE_MAPPING 1 #endif #ifndef LOW_RES_LUMINANCE_MIPS # define LOW_RES_LUMINANCE_MIPS 7 #endif #ifndef TONE_MAPPING_MODE # define TONE_MAPPING_MODE TONE_MAPPING_MODE_REINHARD_MOD #endif #ifndef LIGHT_ADAPTATION # define LIGHT_ADAPTATION 1 #endif #define INVALID_EPIPOLAR_LINE float4(-1000,-1000, -100, -100) //-------------------------------------------------------------------------------------- // Texture samplers //-------------------------------------------------------------------------------------- SamplerState samLinearBorder0 : register( s1 ) { Filter = MIN_MAG_MIP_LINEAR; AddressU = Border; AddressV = Border; BorderColor = float4(0.0, 0.0, 0.0, 0.0); }; SamplerComparisonState samComparison : register( s2 ) { Filter = COMPARISON_MIN_MAG_LINEAR_MIP_POINT; AddressU = Border; AddressV = Border; ComparisonFunc = GREATER; BorderColor = float4(0.0, 0.0, 0.0, 0.0); }; SamplerState samPointClamp : register( s3 ); //-------------------------------------------------------------------------------------- // Depth stencil states //-------------------------------------------------------------------------------------- // Depth stencil state disabling depth test DepthStencilState DSS_NoDepthTest { DepthEnable = false; DepthWriteMask = ZERO; }; DepthStencilState DSS_NoDepthTestIncrStencil { DepthEnable = false; DepthWriteMask = ZERO; STENCILENABLE = true; FRONTFACESTENCILFUNC = ALWAYS; BACKFACESTENCILFUNC = ALWAYS; FRONTFACESTENCILPASS = INCR; BACKFACESTENCILPASS = INCR; }; DepthStencilState DSS_NoDepth_StEqual_IncrStencil { DepthEnable = false; DepthWriteMask = ZERO; STENCILENABLE = true; FRONTFACESTENCILFUNC = EQUAL; BACKFACESTENCILFUNC = EQUAL; FRONTFACESTENCILPASS = INCR; BACKFACESTENCILPASS = INCR; FRONTFACESTENCILFAIL = KEEP; BACKFACESTENCILFAIL = KEEP; }; DepthStencilState DSS_NoDepth_StEqual_KeepStencil { DepthEnable = false; DepthWriteMask = ZERO; STENCILENABLE = true; FRONTFACESTENCILFUNC = EQUAL; BACKFACESTENCILFUNC = EQUAL; FRONTFACESTENCILPASS = KEEP; BACKFACESTENCILPASS = KEEP; FRONTFACESTENCILFAIL = KEEP; BACKFACESTENCILFAIL = KEEP; }; //-------------------------------------------------------------------------------------- // Rasterizer states //-------------------------------------------------------------------------------------- // Rasterizer state for solid fill mode with no culling RasterizerState RS_SolidFill_NoCull { FILLMODE = Solid; CullMode = NONE; }; // Blend state disabling blending BlendState NoBlending { BlendEnable[0] = FALSE; BlendEnable[1] = FALSE; BlendEnable[2] = FALSE; }; float2 ProjToUV(in float2 f2ProjSpaceXY) { return float2(0.5, 0.5) + float2(0.5, -0.5) * f2ProjSpaceXY; } float2 UVToProj(in float2 f2UV) { return float2(-1.0, 1.0) + float2(2.0, -2.0) * f2UV; } float GetCamSpaceZ(in float2 ScreenSpaceUV) { return g_tex2DCamSpaceZ.SampleLevel(samLinearClamp, ScreenSpaceUV, 0); } float3 Uncharted2Tonemap(float3 x) { // http://www.gdcvault.com/play/1012459/Uncharted_2__HDR_Lighting // http://filmicgames.com/archives/75 - the coefficients are from here float A = 0.15; // Shoulder Strength float B = 0.50; // Linear Strength float C = 0.10; // Linear Angle float D = 0.20; // Toe Strength float E = 0.02; // Toe Numerator float F = 0.30; // Toe Denominator return ((x*(A*x+C*B)+D*E)/(x*(A*x+B)+D*F))-E/F; // E/F = Toe Angle } float3 ToneMap(in float3 f3Color) { float fAveLogLum = GetAverageSceneLuminance(); //const float middleGray = 1.03 - 2 / (2 + log10(fAveLogLum+1)); const float middleGray = g_PPAttribs.m_fMiddleGray; // Compute scale factor such that average luminance maps to middle gray float fLumScale = middleGray / fAveLogLum; f3Color = max(f3Color, 0); float fInitialPixelLum = max(dot(RGB_TO_LUMINANCE, f3Color), 1e-10); float fScaledPixelLum = fInitialPixelLum * fLumScale; float3 f3ScaledColor = f3Color * fLumScale; float whitePoint = g_PPAttribs.m_fWhitePoint; #if TONE_MAPPING_MODE == TONE_MAPPING_MODE_EXP float fToneMappedLum = 1.0 - exp( -fScaledPixelLum ); return fToneMappedLum * pow(f3Color / fInitialPixelLum, g_PPAttribs.m_fLuminanceSaturation); #elif TONE_MAPPING_MODE == TONE_MAPPING_MODE_REINHARD || TONE_MAPPING_MODE == TONE_MAPPING_MODE_REINHARD_MOD // http://www.cs.utah.edu/~reinhard/cdrom/tonemap.pdf // http://imdoingitwrong.wordpress.com/2010/08/19/why-reinhard-desaturates-my-blacks-3/ // http://content.gpwiki.org/index.php/D3DBook:High-Dynamic_Range_Rendering float L_xy = fScaledPixelLum; # if TONE_MAPPING_MODE == TONE_MAPPING_MODE_REINHARD float fToneMappedLum = L_xy / (1 + L_xy); # else float fToneMappedLum = L_xy * (1 + L_xy / (whitePoint*whitePoint)) / (1 + L_xy); # endif return fToneMappedLum * pow(f3Color / fInitialPixelLum, g_PPAttribs.m_fLuminanceSaturation); #elif TONE_MAPPING_MODE == TONE_MAPPING_MODE_UNCHARTED2 // http://filmicgames.com/archives/75 float ExposureBias = 2.0f; float3 curr = Uncharted2Tonemap(ExposureBias*f3ScaledColor); float3 whiteScale = 1.0f/Uncharted2Tonemap(whitePoint); return curr*whiteScale; #elif TONE_MAPPING_MODE == TONE_MAPPING_FILMIC_ALU // http://www.gdcvault.com/play/1012459/Uncharted_2__HDR_Lighting float3 f3ToneMappedColor = max(0, f3ScaledColor - 0.004f); f3ToneMappedColor = (f3ToneMappedColor * (6.2f * f3ToneMappedColor + 0.5f)) / (f3ToneMappedColor * (6.2f * f3ToneMappedColor + 1.7f)+ 0.06f); // result has 1/2.2 gamma baked in return pow(f3ToneMappedColor, 2.2f); #elif TONE_MAPPING_MODE == TONE_MAPPING_LOGARITHMIC // http://www.mpi-inf.mpg.de/resources/tmo/logmap/logmap.pdf float fToneMappedLum = log10(1 + fScaledPixelLum) / log10(1 + whitePoint); return fToneMappedLum * pow(f3Color / fInitialPixelLum, g_PPAttribs.m_fLuminanceSaturation); #elif TONE_MAPPING_MODE == TONE_MAPPING_ADAPTIVE_LOG // http://www.mpi-inf.mpg.de/resources/tmo/logmap/logmap.pdf float Bias = 0.85; float fToneMappedLum = 1 / log10(1 + whitePoint) * log(1 + fScaledPixelLum) / log( 2 + 8 * pow( fScaledPixelLum / whitePoint, log(Bias) / log(0.5f)) ); return fToneMappedLum * pow(f3Color / fInitialPixelLum, g_PPAttribs.m_fLuminanceSaturation); #endif } float4 UpdateAverageLuminancePS() : SV_Target { #if LIGHT_ADAPTATION const float fAdaptationRate = 1.f; float fNewLuminanceWeight = 1 - exp( - fAdaptationRate * g_MiscParams.fElapsedTime ); #else float fNewLuminanceWeight = 1; #endif return float4( exp( g_tex2DLowResLuminance.Load( int3(0,0,LOW_RES_LUMINANCE_MIPS-1) ) ), 0, 0, fNewLuminanceWeight ); } float3 ProjSpaceXYToWorldSpace(in float2 f2PosPS) { // We can sample camera space z texture using bilinear filtering float fCamSpaceZ = g_tex2DCamSpaceZ.SampleLevel(samLinearClamp, ProjToUV(f2PosPS), 0); return ProjSpaceXYZToWorldSpace(float3(f2PosPS, fCamSpaceZ)); } float3 WorldSpaceToShadowMapUV(in float3 f3PosWS, in matrix mWorldToShadowMapUVDepth) { float4 f4ShadowMapUVDepth = mul( float4(f3PosWS, 1), mWorldToShadowMapUVDepth ); // Shadow map projection matrix is orthographic, so we do not need to divide by w //f4ShadowMapUVDepth.xyz /= f4ShadowMapUVDepth.w; // Applying depth bias results in light leaking through the opaque objects when looking directly // at the light source return f4ShadowMapUVDepth.xyz; } struct SScreenSizeQuadVSOutput { float4 m_f4Pos : SV_Position; float2 m_f2PosPS : PosPS; // Position in projection space [-1,1]x[-1,1] float m_fInstID : InstanceID; }; SScreenSizeQuadVSOutput GenerateScreenSizeQuadVS(in uint VertexId : SV_VertexID, in uint InstID : SV_InstanceID) { float4 MinMaxUV = float4(-1, -1, 1, 1); SScreenSizeQuadVSOutput Verts[4] = { {float4(MinMaxUV.xy, 1.0, 1.0), MinMaxUV.xy, InstID}, {float4(MinMaxUV.xw, 1.0, 1.0), MinMaxUV.xw, InstID}, {float4(MinMaxUV.zy, 1.0, 1.0), MinMaxUV.zy, InstID}, {float4(MinMaxUV.zw, 1.0, 1.0), MinMaxUV.zw, InstID} }; return Verts[VertexId]; } float ReconstructCameraSpaceZPS(SScreenSizeQuadVSOutput In) : SV_Target { float fDepth = g_tex2DDepthBuffer.Load( uint3(In.m_f4Pos.xy,0) ); float fCamSpaceZ = g_CameraAttribs.mProj[3][2]/(fDepth - g_CameraAttribs.mProj[2][2]); return fCamSpaceZ; }; technique11 ReconstructCameraSpaceZ { pass { SetBlendState( NoBlending, float4( 0.0f, 0.0f, 0.0f, 0.0f ), 0xFFFFFFFF ); SetRasterizerState( RS_SolidFill_NoCull ); SetDepthStencilState( DSS_NoDepthTest, 0 ); SetVertexShader( CompileShader(vs_5_0, GenerateScreenSizeQuadVS() ) ); SetGeometryShader( NULL ); SetPixelShader( CompileShader(ps_5_0, ReconstructCameraSpaceZPS() ) ); } } const float4 GetOutermostScreenPixelCoords() { // The outermost visible screen pixels centers do not lie exactly on the boundary (+1 or -1), but are biased by // 0.5 screen pixel size inwards // // 2.0 // |<---------------------------------------------------------------------->| // // 2.0/Res // |<--------->| // | X | X | X | ... | X | X | // -1 | | +1 // | | // | | // -1 + 1.0/Res +1 - 1.0/Res // // Using shader macro is much more efficient than using constant buffer variable // because the compiler is able to optimize the code more aggressively // return float4(-1,-1,1,1) + float4(1, 1, -1, -1)/g_PPAttribs.m_f2ScreenResolution.xyxy; return float4(-1,-1,1,1) + float4(1, 1, -1, -1) / SCREEN_RESLOUTION.xyxy; } // When checking if a point is inside the screen, we must test against // the biased screen boundaries bool IsValidScreenLocation(in float2 f2XY) { const float SAFETY_EPSILON = 0.2f; return all( abs(f2XY) <= 1.f - (1.f - SAFETY_EPSILON) / SCREEN_RESLOUTION.xy ); } // This function computes entry point of the epipolar line given its exit point // // g_LightAttribs.f4LightScreenPos // * // \ // \ f2EntryPoint // __\/___ // | \ | // | \ | // |_____\_| // | | // | f2ExitPoint // | // Exit boundary float2 GetEpipolarLineEntryPoint(float2 f2ExitPoint) { float2 f2EntryPoint; //if( IsValidScreenLocation(g_LightAttribs.f4LightScreenPos.xy) ) if( g_LightAttribs.bIsLightOnScreen ) { // If light source is on the screen, its location is entry point for each epipolar line f2EntryPoint = g_LightAttribs.f4LightScreenPos.xy; } else { // If light source is outside the screen, we need to compute intersection of the ray with // the screen boundaries // Compute direction from the light source to the exit point // Note that exit point must be located on shrinked screen boundary float2 f2RayDir = f2ExitPoint.xy - g_LightAttribs.f4LightScreenPos.xy; float fDistToExitBoundary = length(f2RayDir); f2RayDir /= fDistToExitBoundary; // Compute signed distances along the ray from the light position to all four boundaries // The distances are computed as follows using vector instructions: // float fDistToLeftBoundary = abs(f2RayDir.x) > 1e-5 ? (-1 - g_LightAttribs.f4LightScreenPos.x) / f2RayDir.x : -FLT_MAX; // float fDistToBottomBoundary = abs(f2RayDir.y) > 1e-5 ? (-1 - g_LightAttribs.f4LightScreenPos.y) / f2RayDir.y : -FLT_MAX; // float fDistToRightBoundary = abs(f2RayDir.x) > 1e-5 ? ( 1 - g_LightAttribs.f4LightScreenPos.x) / f2RayDir.x : -FLT_MAX; // float fDistToTopBoundary = abs(f2RayDir.y) > 1e-5 ? ( 1 - g_LightAttribs.f4LightScreenPos.y) / f2RayDir.y : -FLT_MAX; // Note that in fact the outermost visible screen pixels do not lie exactly on the boundary (+1 or -1), but are biased by // 0.5 screen pixel size inwards. Using these adjusted boundaries improves precision and results in // smaller number of pixels which require inscattering correction float4 f4Boundaries = GetOutermostScreenPixelCoords(); bool4 b4IsCorrectIntersectionFlag = abs(f2RayDir.xyxy) > 1e-5; float4 f4DistToBoundaries = (f4Boundaries - g_LightAttribs.f4LightScreenPos.xyxy) / (f2RayDir.xyxy + !b4IsCorrectIntersectionFlag); // Addition of !b4IsCorrectIntersectionFlag is required to prevent divison by zero // Note that such incorrect lanes will be masked out anyway // We now need to find first intersection BEFORE the intersection with the exit boundary // This means that we need to find maximum intersection distance which is less than fDistToBoundary // We thus need to skip all boundaries, distance to which is greater than the distance to exit boundary // Using -FLT_MAX as the distance to these boundaries will result in skipping them: b4IsCorrectIntersectionFlag = b4IsCorrectIntersectionFlag && ( f4DistToBoundaries < (fDistToExitBoundary - 1e-4) ); f4DistToBoundaries = b4IsCorrectIntersectionFlag * f4DistToBoundaries + !b4IsCorrectIntersectionFlag * float4(-FLT_MAX, -FLT_MAX, -FLT_MAX, -FLT_MAX); float fFirstIntersecDist = 0; fFirstIntersecDist = max(fFirstIntersecDist, f4DistToBoundaries.x); fFirstIntersecDist = max(fFirstIntersecDist, f4DistToBoundaries.y); fFirstIntersecDist = max(fFirstIntersecDist, f4DistToBoundaries.z); fFirstIntersecDist = max(fFirstIntersecDist, f4DistToBoundaries.w); // The code above is equivalent to the following lines: // fFirstIntersecDist = fDistToLeftBoundary < fDistToBoundary-1e-4 ? max(fFirstIntersecDist, fDistToLeftBoundary) : fFirstIntersecDist; // fFirstIntersecDist = fDistToBottomBoundary < fDistToBoundary-1e-4 ? max(fFirstIntersecDist, fDistToBottomBoundary) : fFirstIntersecDist; // fFirstIntersecDist = fDistToRightBoundary < fDistToBoundary-1e-4 ? max(fFirstIntersecDist, fDistToRightBoundary) : fFirstIntersecDist; // fFirstIntersecDist = fDistToTopBoundary < fDistToBoundary-1e-4 ? max(fFirstIntersecDist, fDistToTopBoundary) : fFirstIntersecDist; // Now we can compute entry point: f2EntryPoint = g_LightAttribs.f4LightScreenPos.xy + f2RayDir * fFirstIntersecDist; // For invalid rays, coordinates are outside [-1,1]x[-1,1] area // and such rays will be discarded // // g_LightAttribs.f4LightScreenPos // * // \| // \-f2EntryPoint // |\ // | \ f2ExitPoint // |__\/___ // | | // | | // |_______| // } return f2EntryPoint; } float4 GenerateSliceEndpointsPS(SScreenSizeQuadVSOutput In) : SV_Target { float2 f2UV = ProjToUV(In.m_f2PosPS); // Note that due to the rasterization rules, UV coordinates are biased by 0.5 texel size. // // 0.5 1.5 2.5 3.5 // | X | X | X | X | .... // 0 1 2 3 4 f2UV * TexDim // X - locations where rasterization happens // // We need to remove this offset. Also clamp to [0,1] to fix fp32 precision issues float fEpipolarSlice = saturate(f2UV.x - 0.5f / (float)NUM_EPIPOLAR_SLICES); // fEpipolarSlice now lies in the range [0, 1 - 1/NUM_EPIPOLAR_SLICES] // 0 defines location in exacatly left top corner, 1 - 1/NUM_EPIPOLAR_SLICES defines // position on the top boundary next to the top left corner uint uiBoundary = clamp(floor( fEpipolarSlice * 4 ), 0, 3); float fPosOnBoundary = frac( fEpipolarSlice * 4 ); bool4 b4BoundaryFlags = bool4( uiBoundary.xxxx == uint4(0,1,2,3) ); // Note that in fact the outermost visible screen pixels do not lie exactly on the boundary (+1 or -1), but are biased by // 0.5 screen pixel size inwards. Using these adjusted boundaries improves precision and results in // samller number of pixels which require inscattering correction float4 f4OutermostScreenPixelCoords = GetOutermostScreenPixelCoords();// xyzw = (left, bottom, right, top) // Check if there can definitely be no correct intersection with the boundary: // // Light.x <= LeftBnd Light.y <= BottomBnd Light.x >= RightBnd Light.y >= TopBnd // * // ____ ____ ____ __/_ // .| | | | | | .* | | // .' |____| |____| |____|.' |____| // * \ // * // Left Boundary Bottom Boundary Right Boundary Top Boundary // bool4 b4IsInvalidBoundary = bool4( (g_LightAttribs.f4LightScreenPos.xyxy - f4OutermostScreenPixelCoords.xyzw) * float4(1,1,-1,-1) <= 0 ); if( dot(b4IsInvalidBoundary, b4BoundaryFlags) ) return INVALID_EPIPOLAR_LINE; // Additinal check above is required to eliminate false epipolar lines which can appear is shown below. // The reason is that we have to use some safety delta when performing check in IsValidScreenLocation() // function. If we do not do this, we will miss valid entry points due to precision issues. // As a result there could appear false entry points which fall into the safety region, but in fact lie // outside the screen boundary: // // LeftBnd-Delta LeftBnd // false epipolar line // | | / // | | / // | |/ X - false entry point // | * // | /| // |------X-|----------- BottomBnd // | / | // | / | // |___/____|___________ BottomBnd-Delta // // // <------ // +1 0,1___________0.75 // | 3 | // | | | A // | |0 2| | // V | | | // -1 |_____1_____| // 0.25 ------> 0.5 // // -1 +1 // // Left Bottom Right Top float4 f4BoundaryXPos = float4( 0, fPosOnBoundary, 1, 1-fPosOnBoundary); float4 f4BoundaryYPos = float4( 1-fPosOnBoundary, 0, fPosOnBoundary, 1); // Select the right coordinates for the boundary float2 f2ExitPointPosOnBnd = float2( dot(f4BoundaryXPos, b4BoundaryFlags), dot(f4BoundaryYPos, b4BoundaryFlags) ); float2 f2ExitPoint = lerp(f4OutermostScreenPixelCoords.xy, f4OutermostScreenPixelCoords.zw, f2ExitPointPosOnBnd); // GetEpipolarLineEntryPoint() gets exit point on SHRINKED boundary float2 f2EntryPoint = GetEpipolarLineEntryPoint(f2ExitPoint); #if OPTIMIZE_SAMPLE_LOCATIONS // If epipolar slice is not invisible, advance its exit point if necessary if( IsValidScreenLocation(f2EntryPoint) ) { // Compute length of the epipolar line in screen pixels: float fEpipolarSliceScreenLen = length( (f2ExitPoint - f2EntryPoint) * SCREEN_RESLOUTION.xy / 2 ); // If epipolar line is too short, update epipolar line exit point to provide 1:1 texel to screen pixel correspondence: f2ExitPoint = f2EntryPoint + (f2ExitPoint - f2EntryPoint) * max((float)MAX_SAMPLES_IN_SLICE / fEpipolarSliceScreenLen, 1); } #endif return float4(f2EntryPoint, f2ExitPoint); } void GenerateCoordinateTexturePS(SScreenSizeQuadVSOutput In, out float2 f2XY : SV_Target0, out float fCamSpaceZ : SV_Target1) { float4 f4SliceEndPoints = g_tex2DSliceEndPoints.Load( int3(In.m_f4Pos.y,0,0) ); // If slice entry point is outside [-1,1]x[-1,1] area, the slice is completely invisible // and we can skip it from further processing. // Note that slice exit point can lie outside the screen, if sample locations are optimized if( !IsValidScreenLocation(f4SliceEndPoints.xy) ) { // Discard invalid slices // Such slices will not be marked in the stencil and as a result will always be skipped discard; } float2 f2UV = ProjToUV(In.m_f2PosPS); // Note that due to the rasterization rules, UV coordinates are biased by 0.5 texel size. // // 0.5 1.5 2.5 3.5 // | X | X | X | X | .... // 0 1 2 3 4 f2UV * f2TexDim // X - locations where rasterization happens // // We need remove this offset: float fSamplePosOnEpipolarLine = f2UV.x - 0.5f / (float)MAX_SAMPLES_IN_SLICE; // fSamplePosOnEpipolarLine is now in the range [0, 1 - 1/MAX_SAMPLES_IN_SLICE] // We need to rescale it to be in [0, 1] fSamplePosOnEpipolarLine *= (float)MAX_SAMPLES_IN_SLICE / ((float)MAX_SAMPLES_IN_SLICE-1.f); fSamplePosOnEpipolarLine = saturate(fSamplePosOnEpipolarLine); // Compute interpolated position between entry and exit points: f2XY = lerp(f4SliceEndPoints.xy, f4SliceEndPoints.zw, fSamplePosOnEpipolarLine); if( !IsValidScreenLocation(f2XY) ) { // Discard pixels that fall behind the screen // This can happen if slice exit point was optimized discard; } // Compute camera space z for current location fCamSpaceZ = GetCamSpaceZ( ProjToUV(f2XY) ); }; technique11 GenerateCoordinateTexture { pass { SetBlendState( NoBlending, float4( 0.0f, 0.0f, 0.0f, 0.0f ), 0xFFFFFFFF ); SetRasterizerState( RS_SolidFill_NoCull ); // Increase stencil value for all valid rays SetDepthStencilState( DSS_NoDepthTestIncrStencil, 0 ); SetVertexShader( CompileShader(vs_5_0, GenerateScreenSizeQuadVS() ) ); SetGeometryShader( NULL ); SetPixelShader( CompileShader(ps_5_0, GenerateCoordinateTexturePS() ) ); } } static const float4 g_f4IncorrectSliceUVDirAndStart = float4(-10000, -10000, 0, 0); float4 RenderSliceUVDirInShadowMapTexturePS(SScreenSizeQuadVSOutput In) : SV_Target { uint uiSliceInd = In.m_f4Pos.x; // Load epipolar slice endpoints float4 f4SliceEndpoints = g_tex2DSliceEndPoints.Load( uint3(uiSliceInd,0,0) ); // All correct entry points are completely inside the [-1+1/W,1-1/W]x[-1+1/H,1-1/H] area if( !IsValidScreenLocation(f4SliceEndpoints.xy) ) return g_f4IncorrectSliceUVDirAndStart; uint uiCascadeInd = In.m_f4Pos.y; matrix mWorldToShadowMapUVDepth = g_LightAttribs.ShadowAttribs.mWorldToShadowMapUVDepth[uiCascadeInd]; // Reconstruct slice exit point position in world space float3 f3SliceExitWS = ProjSpaceXYZToWorldSpace( float3(f4SliceEndpoints.zw, g_LightAttribs.ShadowAttribs.Cascades[uiCascadeInd].f4StartEndZ.y) ); // Transform it to the shadow map UV float2 f2SliceExitUV = WorldSpaceToShadowMapUV(f3SliceExitWS, mWorldToShadowMapUVDepth).xy; // Compute camera position in shadow map UV space float2 f2SliceOriginUV = WorldSpaceToShadowMapUV(g_CameraAttribs.f4CameraPos.xyz, mWorldToShadowMapUVDepth).xy; // Compute slice direction in shadow map UV space float2 f2SliceDir = f2SliceExitUV - f2SliceOriginUV; f2SliceDir /= max(abs(f2SliceDir.x), abs(f2SliceDir.y)); float4 f4BoundaryMinMaxXYXY = float4(0,0,1,1) + float4(0.5, 0.5, -0.5, -0.5)*g_PPAttribs.m_f2ShadowMapTexelSize.xyxy; if( any( (f2SliceOriginUV.xyxy - f4BoundaryMinMaxXYXY) * float4( 1, 1, -1, -1) < 0 ) ) { // If slice origin in UV coordinates falls beyond [0,1]x[0,1] region, we have // to continue the ray and intersect it with this rectangle // // f2SliceOriginUV // * // \ // \ New f2SliceOriginUV // 1 __\/___ // | | // | | // 0 |_______| // 0 1 // // First, compute signed distances from the slice origin to all four boundaries bool4 b4IsValidIsecFlag = abs(f2SliceDir.xyxy) > 1e-6; float4 f4DistToBoundaries = (f4BoundaryMinMaxXYXY - f2SliceOriginUV.xyxy) / (f2SliceDir.xyxy + !b4IsValidIsecFlag); //We consider only intersections in the direction of the ray b4IsValidIsecFlag = b4IsValidIsecFlag && (f4DistToBoundaries>0); // Compute the second intersection coordinate float4 f4IsecYXYX = f2SliceOriginUV.yxyx + f4DistToBoundaries * f2SliceDir.yxyx; // Select only these coordinates that fall onto the boundary b4IsValidIsecFlag = b4IsValidIsecFlag && (f4IsecYXYX >= f4BoundaryMinMaxXYXY.yxyx) && (f4IsecYXYX <= f4BoundaryMinMaxXYXY.wzwz); // Replace distances to all incorrect boundaries with the large value f4DistToBoundaries = b4IsValidIsecFlag * f4DistToBoundaries + !b4IsValidIsecFlag * float4(+FLT_MAX, +FLT_MAX, +FLT_MAX, +FLT_MAX); // Select the closest valid intersection float2 f2MinDist = min(f4DistToBoundaries.xy, f4DistToBoundaries.zw); float fMinDist = min(f2MinDist.x, f2MinDist.y); // Update origin f2SliceOriginUV = f2SliceOriginUV + fMinDist * f2SliceDir; } f2SliceDir *= g_PPAttribs.m_f2ShadowMapTexelSize; return float4(f2SliceDir, f2SliceOriginUV); } technique11 RenderSliceUVDirInShadowMapTexture { pass p0 { SetBlendState( NoBlending, float4( 0.0f, 0.0f, 0.0f, 0.0f ), 0xFFFFFFFF ); SetRasterizerState( RS_SolidFill_NoCull ); // Only interpolation samples will not be discarded and increase the stencil value SetDepthStencilState( DSS_NoDepthTest, 0 ); SetVertexShader( CompileShader(vs_5_0, GenerateScreenSizeQuadVS() ) ); SetGeometryShader( NULL ); SetPixelShader( CompileShader(ps_5_0, RenderSliceUVDirInShadowMapTexturePS() ) ); } } // Note that min/max shadow map does not contain finest resolution level // The first level it contains corresponds to step == 2 MIN_MAX_DATA_FORMAT InitializeMinMaxShadowMapPS(SScreenSizeQuadVSOutput In) : SV_Target { uint uiSliceInd; float fCascadeInd; #if USE_COMBINED_MIN_MAX_TEXTURE fCascadeInd = floor(In.m_f4Pos.y / NUM_EPIPOLAR_SLICES); uiSliceInd = In.m_f4Pos.y - fCascadeInd * NUM_EPIPOLAR_SLICES; fCascadeInd += g_PPAttribs.m_fFirstCascade; #else uiSliceInd = In.m_f4Pos.y; fCascadeInd = g_MiscParams.fCascadeInd; #endif // Load slice direction in shadow map float4 f4SliceUVDirAndOrigin = g_tex2DSliceUVDirAndOrigin.Load( uint3(uiSliceInd, fCascadeInd, 0) ); // Calculate current sample position on the ray float2 f2CurrUV = f4SliceUVDirAndOrigin.zw + f4SliceUVDirAndOrigin.xy * floor(In.m_f4Pos.x) * 2.f; float4 f4MinDepth = 1; float4 f4MaxDepth = 0; // Gather 8 depths which will be used for PCF filtering for this sample and its immediate neighbor // along the epipolar slice // Note that if the sample is located outside the shadow map, Gather() will return 0 as // specified by the samLinearBorder0. As a result volumes outside the shadow map will always be lit for( float i=0; i<=1; ++i ) { float4 f4Depths = g_tex2DLightSpaceDepthMap.Gather(samLinearBorder0, float3(f2CurrUV + i * f4SliceUVDirAndOrigin.xy, fCascadeInd) ); f4MinDepth = min(f4MinDepth, f4Depths); f4MaxDepth = max(f4MaxDepth, f4Depths); } f4MinDepth.xy = min(f4MinDepth.xy, f4MinDepth.zw); f4MinDepth.x = min(f4MinDepth.x, f4MinDepth.y); f4MaxDepth.xy = max(f4MaxDepth.xy, f4MaxDepth.zw); f4MaxDepth.x = max(f4MaxDepth.x, f4MaxDepth.y); #if !IS_32BIT_MIN_MAX_MAP const float R16_UNORM_PRECISION = 1.f / (float)(1<<16); f4MinDepth.x = floor(f4MinDepth.x/R16_UNORM_PRECISION)*R16_UNORM_PRECISION; f4MaxDepth.x = ceil(f4MaxDepth.x/R16_UNORM_PRECISION)*R16_UNORM_PRECISION; #endif return float2(f4MinDepth.x, f4MaxDepth.x); } // 1D min max mip map is arranged as follows: // // g_MiscParams.ui4SrcDstMinMaxLevelOffset.x // | // | g_MiscParams.ui4SrcDstMinMaxLevelOffset.z // |_______|____ __ // | | | | // | | | | // | | | | // | | | | // |_______|____|__| // |<----->|<-->| // | | // | uiMinMaxShadowMapResolution/ // uiMinMaxShadowMapResolution/2 // MIN_MAX_DATA_FORMAT ComputeMinMaxShadowMapLevelPS(SScreenSizeQuadVSOutput In) : SV_Target { uint2 uiDstSampleInd = uint2(In.m_f4Pos.xy); uint2 uiSrcSample0Ind = uint2(g_MiscParams.ui4SrcDstMinMaxLevelOffset.x + (uiDstSampleInd.x - g_MiscParams.ui4SrcDstMinMaxLevelOffset.z)*2, uiDstSampleInd.y); uint2 uiSrcSample1Ind = uiSrcSample0Ind + uint2(1,0); MIN_MAX_DATA_FORMAT fnMinMaxDepth0 = g_tex2DMinMaxLightSpaceDepth.Load( uint3(uiSrcSample0Ind,0) ); MIN_MAX_DATA_FORMAT fnMinMaxDepth1 = g_tex2DMinMaxLightSpaceDepth.Load( uint3(uiSrcSample1Ind,0) ); float2 f2MinMaxDepth; f2MinMaxDepth.x = min(fnMinMaxDepth0.x, fnMinMaxDepth1.x); f2MinMaxDepth.y = max(fnMinMaxDepth0.y, fnMinMaxDepth1.y); return f2MinMaxDepth; } technique11 BuildMinMaxMipMap { pass PInitializeMinMaxShadowMap { SetBlendState( NoBlending, float4( 0.0f, 0.0f, 0.0f, 0.0f ), 0xFFFFFFFF ); SetRasterizerState( RS_SolidFill_NoCull ); // Only interpolation samples will not be discarded and increase the stencil value SetDepthStencilState( DSS_NoDepthTest, 0 ); SetVertexShader( CompileShader(vs_5_0, GenerateScreenSizeQuadVS() ) ); SetGeometryShader( NULL ); SetPixelShader( CompileShader(ps_5_0, InitializeMinMaxShadowMapPS() ) ); } pass PComputeMinMaxShadowMapLevel { SetBlendState( NoBlending, float4( 0.0f, 0.0f, 0.0f, 0.0f ), 0xFFFFFFFF ); SetRasterizerState( RS_SolidFill_NoCull ); // Only interpolation samples will not be discarded and increase the stencil value SetDepthStencilState( DSS_NoDepthTest, 0 ); SetVertexShader( CompileShader(vs_5_0, GenerateScreenSizeQuadVS() ) ); SetGeometryShader( NULL ); SetPixelShader( CompileShader(ps_5_0, ComputeMinMaxShadowMapLevelPS() ) ); } } void MarkRayMarchingSamplesInStencilPS(SScreenSizeQuadVSOutput In) { uint2 ui2InterpolationSources = g_tex2DInterpolationSource.Load( uint3(In.m_f4Pos.xy,0) ); // Ray marching samples are interpolated from themselves, so it is easy to detect them: if( ui2InterpolationSources.x != ui2InterpolationSources.y ) discard; } technique11 MarkRayMarchingSamplesInStencil { pass { SetBlendState( NoBlending, float4( 0.0f, 0.0f, 0.0f, 0.0f ), 0xFFFFFFFF ); SetRasterizerState( RS_SolidFill_NoCull ); // Only interpolation samples will not be discarded and increase the stencil value SetDepthStencilState( DSS_NoDepth_StEqual_IncrStencil, 1 ); SetVertexShader( CompileShader(vs_5_0, GenerateScreenSizeQuadVS() ) ); SetGeometryShader( NULL ); SetPixelShader( CompileShader(ps_5_0, MarkRayMarchingSamplesInStencilPS() ) ); } } float3 InterpolateIrradiancePS(SScreenSizeQuadVSOutput In) : SV_Target { uint uiSampleInd = In.m_f4Pos.x; uint uiSliceInd = In.m_f4Pos.y; // Get interpolation sources uint2 ui2InterpolationSources = g_tex2DInterpolationSource.Load( uint3(uiSampleInd, uiSliceInd, 0) ); float fInterpolationPos = float(uiSampleInd - ui2InterpolationSources.x) / float( max(ui2InterpolationSources.y - ui2InterpolationSources.x,1) ); float3 f3SrcInsctr0 = g_tex2DInitialInsctrIrradiance.Load( uint3(ui2InterpolationSources.x, uiSliceInd, 0) ); float3 f3SrcInsctr1 = g_tex2DInitialInsctrIrradiance.Load( uint3(ui2InterpolationSources.y, uiSliceInd, 0)); // Ray marching samples are interpolated from themselves return lerp(f3SrcInsctr0, f3SrcInsctr1, fInterpolationPos); } technique11 InterpolateIrradiance { pass { SetBlendState( NoBlending, float4( 0.0f, 0.0f, 0.0f, 0.0f ), 0xFFFFFFFF ); SetRasterizerState( RS_SolidFill_NoCull ); SetDepthStencilState( DSS_NoDepthTest, 0 ); SetVertexShader( CompileShader(vs_5_0, GenerateScreenSizeQuadVS() ) ); SetGeometryShader( NULL ); SetPixelShader( CompileShader(ps_5_0, InterpolateIrradiancePS() ) ); } } void UnwarpEpipolarInsctrImage( SScreenSizeQuadVSOutput In, in float fCamSpaceZ, out float3 f3Inscattering, out float3 f3Extinction ) { // Compute direction of the ray going from the light through the pixel float2 f2RayDir = normalize( In.m_f2PosPS - g_LightAttribs.f4LightScreenPos.xy ); // Find, which boundary the ray intersects. For this, we will // find which two of four half spaces the f2RayDir belongs to // Each of four half spaces is produced by the line connecting one of four // screen corners and the current pixel: // ________________ _______'________ ________________ // |' . '| | ' | | | // | ' . ' | | ' | . | | // | ' . ' | | ' | '|. hs1 | // | *. | | * hs0 | | '*. | // | ' ' . | | ' | | ' . | // | ' ' . | | ' | | ' . | // |'____________ '_| |'_______________| | ____________ '_. // ' ' // ________________ . '________________ // | . '| |' | // | hs2 . ' | | ' | // | . ' | | ' | // | . * | | * | // . ' | | ' | // | | | hs3 ' | // |________________| |______'_________| // ' // The equations for the half spaces are the following: //bool hs0 = (In.m_f2PosPS.x - (-1)) * f2RayDir.y < f2RayDir.x * (In.m_f2PosPS.y - (-1)); //bool hs1 = (In.m_f2PosPS.x - (1)) * f2RayDir.y < f2RayDir.x * (In.m_f2PosPS.y - (-1)); //bool hs2 = (In.m_f2PosPS.x - (1)) * f2RayDir.y < f2RayDir.x * (In.m_f2PosPS.y - (1)); //bool hs3 = (In.m_f2PosPS.x - (-1)) * f2RayDir.y < f2RayDir.x * (In.m_f2PosPS.y - (1)); // Note that in fact the outermost visible screen pixels do not lie exactly on the boundary (+1 or -1), but are biased by // 0.5 screen pixel size inwards. Using these adjusted boundaries improves precision and results in // smaller number of pixels which require inscattering correction float4 f4Boundaries = GetOutermostScreenPixelCoords();//left, bottom, right, top float4 f4HalfSpaceEquationTerms = (In.m_f2PosPS.xxyy - f4Boundaries.xzyw/*float4(-1,1,-1,1)*/) * f2RayDir.yyxx; bool4 b4HalfSpaceFlags = f4HalfSpaceEquationTerms.xyyx < f4HalfSpaceEquationTerms.zzww; // Now compute mask indicating which of four sectors the f2RayDir belongs to and consiquently // which border the ray intersects: // ________________ // |' . '| 0 : hs3 && !hs0 // | ' 3 . ' | 1 : hs0 && !hs1 // | ' . ' | 2 : hs1 && !hs2 // |0 *. 2 | 3 : hs2 && !hs3 // | ' ' . | // | ' 1 ' . | // |'____________ '_| // bool4 b4SectorFlags = b4HalfSpaceFlags.wxyz && !b4HalfSpaceFlags.xyzw; // Note that b4SectorFlags now contains true (1) for the exit boundary and false (0) for 3 other // Compute distances to boundaries according to following lines: //float fDistToLeftBoundary = abs(f2RayDir.x) > 1e-5 ? ( -1 - g_LightAttribs.f4LightScreenPos.x) / f2RayDir.x : -FLT_MAX; //float fDistToBottomBoundary = abs(f2RayDir.y) > 1e-5 ? ( -1 - g_LightAttribs.f4LightScreenPos.y) / f2RayDir.y : -FLT_MAX; //float fDistToRightBoundary = abs(f2RayDir.x) > 1e-5 ? ( 1 - g_LightAttribs.f4LightScreenPos.x) / f2RayDir.x : -FLT_MAX; //float fDistToTopBoundary = abs(f2RayDir.y) > 1e-5 ? ( 1 - g_LightAttribs.f4LightScreenPos.y) / f2RayDir.y : -FLT_MAX; float4 f4DistToBoundaries = ( f4Boundaries - g_LightAttribs.f4LightScreenPos.xyxy ) / (f2RayDir.xyxy + float4( abs(f2RayDir.xyxy)<1e-6 ) ); // Select distance to the exit boundary: float fDistToExitBoundary = dot( b4SectorFlags, f4DistToBoundaries ); // Compute exit point on the boundary: float2 f2ExitPoint = g_LightAttribs.f4LightScreenPos.xy + f2RayDir * fDistToExitBoundary; // Compute epipolar slice for each boundary: //if( LeftBoundary ) // fEpipolarSlice = 0.0 - (LeftBoudaryIntersecPoint.y - 1 )/2 /4; //else if( BottomBoundary ) // fEpipolarSlice = 0.25 + (BottomBoudaryIntersecPoint.x - (-1))/2 /4; //else if( RightBoundary ) // fEpipolarSlice = 0.5 + (RightBoudaryIntersecPoint.y - (-1))/2 /4; //else if( TopBoundary ) // fEpipolarSlice = 0.75 - (TopBoudaryIntersecPoint.x - 1 )/2 /4; float4 f4EpipolarSlice = float4(0, 0.25, 0.5, 0.75) + saturate( (f2ExitPoint.yxyx - f4Boundaries.wxyz)*float4(-1, +1, +1, -1) / (f4Boundaries.wzwz - f4Boundaries.yxyx) ) / 4.0; // Select the right value: float fEpipolarSlice = dot(b4SectorFlags, f4EpipolarSlice); // Now find two closest epipolar slices, from which we will interpolate // First, find index of the slice which precedes our slice // Note that 0 <= fEpipolarSlice <= 1, and both 0 and 1 refer to the first slice float fPrecedingSliceInd = min( floor(fEpipolarSlice*NUM_EPIPOLAR_SLICES), NUM_EPIPOLAR_SLICES-1 ); // Compute EXACT texture coordinates of preceding and succeeding slices and their weights // Note that slice 0 is stored in the first texel which has exact texture coordinate 0.5/NUM_EPIPOLAR_SLICES // (search for "fEpipolarSlice = saturate(f2UV.x - 0.5f / (float)NUM_EPIPOLAR_SLICES)"): float fSrcSliceV[2]; // Compute V coordinate to refer exactly the center of the slice row fSrcSliceV[0] = fPrecedingSliceInd/NUM_EPIPOLAR_SLICES + 0.5f/(float)NUM_EPIPOLAR_SLICES; // Use frac() to wrap around to the first slice from the next-to-last slice: fSrcSliceV[1] = frac( fSrcSliceV[0] + 1.f/(float)NUM_EPIPOLAR_SLICES ); // Compute slice weights float fSliceWeights[2]; fSliceWeights[1] = (fEpipolarSlice*NUM_EPIPOLAR_SLICES) - fPrecedingSliceInd; fSliceWeights[0] = 1 - fSliceWeights[1]; f3Inscattering = 0; f3Extinction = 0; float fTotalWeight = 0; [unroll] for(int i=0; i<2; ++i) { // Load epipolar line endpoints float4 f4SliceEndpoints = g_tex2DSliceEndPoints.SampleLevel( samLinearClamp, float2(fSrcSliceV[i], 0.5), 0 ); // Compute line direction on the screen float2 f2SliceDir = f4SliceEndpoints.zw - f4SliceEndpoints.xy; float fSliceLenSqr = dot(f2SliceDir, f2SliceDir); // Project current pixel onto the epipolar line float fSamplePosOnLine = dot((In.m_f2PosPS - f4SliceEndpoints.xy), f2SliceDir) / max(fSliceLenSqr, 1e-8); // Compute index of the slice on the line // Note that the first sample on the line (fSamplePosOnLine==0) is exactly the Entry Point, while // the last sample (fSamplePosOnLine==1) is exactly the Exit Point // (search for "fSamplePosOnEpipolarLine *= (float)MAX_SAMPLES_IN_SLICE / ((float)MAX_SAMPLES_IN_SLICE-1.f)") float fSampleInd = fSamplePosOnLine * (float)(MAX_SAMPLES_IN_SLICE-1); // We have to manually perform bilateral filtering of the scattered radiance texture to // eliminate artifacts at depth discontinuities float fPrecedingSampleInd = floor(fSampleInd); // Get bilinear filtering weight float fUWeight = fSampleInd - fPrecedingSampleInd; // Get texture coordinate of the left source texel. Again, offset by 0.5 is essential // to align with the texel center float fPrecedingSampleU = (fPrecedingSampleInd + 0.5) / (float)(MAX_SAMPLES_IN_SLICE); float2 f2SctrColorUV = float2(fPrecedingSampleU, fSrcSliceV[i]); // Gather 4 camera space z values // Note that we need to bias f2SctrColorUV by 0.5 texel size to refer the location between all four texels and // get the required values for sure // The values in float4, which Gather() returns are arranged as follows: // _______ _______ // | | | // | x | y | // |_______o_______| o gather location // | | | // | *w | z | * f2SctrColorUV // |_______|_______| // |<----->| // 1/f2ScatteredColorTexDim.x // x == g_tex2DEpipolarCamSpaceZ.SampleLevel(samPointClamp, f2SctrColorUV, 0, int2(0,1)) // y == g_tex2DEpipolarCamSpaceZ.SampleLevel(samPointClamp, f2SctrColorUV, 0, int2(1,1)) // z == g_tex2DEpipolarCamSpaceZ.SampleLevel(samPointClamp, f2SctrColorUV, 0, int2(1,0)) // w == g_tex2DEpipolarCamSpaceZ.SampleLevel(samPointClamp, f2SctrColorUV, 0, int2(0,0)) const float2 f2ScatteredColorTexDim = float2(MAX_SAMPLES_IN_SLICE, NUM_EPIPOLAR_SLICES); float2 f2SrcLocationsCamSpaceZ = g_tex2DEpipolarCamSpaceZ.Gather(samLinearClamp, f2SctrColorUV + float2(0.5, 0.5) / f2ScatteredColorTexDim.xy).wz; // Compute depth weights in a way that if the difference is less than the threshold, the weight is 1 and // the weights fade out to 0 as the difference becomes larger than the threshold: float2 f2MaxZ = max( f2SrcLocationsCamSpaceZ, max(fCamSpaceZ,1) ); float2 f2DepthWeights = saturate( g_PPAttribs.m_fRefinementThreshold / max( abs(fCamSpaceZ-f2SrcLocationsCamSpaceZ)/f2MaxZ, g_PPAttribs.m_fRefinementThreshold ) ); // Note that if the sample is located outside the [-1,1]x[-1,1] area, the sample is invalid and fCurrCamSpaceZ == fInvalidCoordinate // Depth weight computed for such sample will be zero f2DepthWeights = pow(f2DepthWeights, 4); // Multiply bilinear weights with the depth weights: float2 f2BilateralUWeights = float2(1-fUWeight, fUWeight) * f2DepthWeights * fSliceWeights[i]; // If the sample projection is behind [0,1], we have to discard this slice // We however must take into account the fact that if at least one sample from the two // bilinear sources is correct, the sample can still be properly computed // // -1 0 1 N-2 N-1 N Sample index // | X | X | X | X | ...... | X | X | X | X | // 1-1/(N-1) 0 1/(N-1) 1 1+1/(N-1) fSamplePosOnLine // | | // |<-------------------Clamp range------------------->| // f2BilateralUWeights *= (abs(fSamplePosOnLine - 0.5) < 0.5 + 1.f / (MAX_SAMPLES_IN_SLICE-1)); // We now need to compute the following weighted summ: //f3FilteredSliceCol = // f2BilateralUWeights.x * g_tex2DScatteredColor.SampleLevel(samPoint, f2SctrColorUV, 0, int2(0,0)) + // f2BilateralUWeights.y * g_tex2DScatteredColor.SampleLevel(samPoint, f2SctrColorUV, 0, int2(1,0)); // We will use hardware to perform bilinear filtering and get this value using single bilinear fetch: // Offset: (x=1,y=0) (x=1,y=0) (x=0,y=0) float fSubpixelUOffset = f2BilateralUWeights.y / max(f2BilateralUWeights.x + f2BilateralUWeights.y, 0.001); fSubpixelUOffset /= f2ScatteredColorTexDim.x; float3 f3FilteredSliceInsctr = (f2BilateralUWeights.x + f2BilateralUWeights.y) * g_tex2DScatteredColor.SampleLevel(samLinearClamp, f2SctrColorUV + float2(fSubpixelUOffset, 0), 0); f3Inscattering += f3FilteredSliceInsctr; #if EXTINCTION_EVAL_MODE == EXTINCTION_EVAL_MODE_EPIPOLAR float3 f3FilteredSliceExtinction = (f2BilateralUWeights.x + f2BilateralUWeights.y) * g_tex2DEpipolarExtinction.SampleLevel(samLinearClamp, f2SctrColorUV + float2(fSubpixelUOffset, 0), 0); f3Extinction += f3FilteredSliceExtinction; #endif // Update total weight fTotalWeight += dot(f2BilateralUWeights, 1); } #if CORRECT_INSCATTERING_AT_DEPTH_BREAKS if( fTotalWeight < 1e-2 ) { // Discarded pixels will keep 0 value in stencil and will be later // processed to correct scattering discard; } #endif f3Inscattering /= fTotalWeight; f3Extinction /= fTotalWeight; } float2 GetDensityIntegralAnalytic(float r, float mu, float d); float3 GetExtinction(in float3 f3StartPos, in float3 f3EndPos); float3 GetExtinctionUnverified(in float3 f3StartPos, in float3 f3EndPos, in float3 f3ViewDir, in float3 f3EarthCentre); float3 ApplyInscatteredRadiancePS(SScreenSizeQuadVSOutput In) : SV_Target { float2 f2UV = ProjToUV(In.m_f2PosPS); float fCamSpaceZ = GetCamSpaceZ( f2UV ); float3 f3Inscttering, f3Extinction; UnwarpEpipolarInsctrImage(In, fCamSpaceZ, f3Inscttering, f3Extinction); float3 f3BackgroundColor = 0; [branch] if( !g_PPAttribs.m_bShowLightingOnly ) { f3BackgroundColor = g_tex2DColorBuffer.SampleLevel( samPointClamp, f2UV, 0).rgb; // fFarPlaneZ is pre-multiplied with 0.999999f f3BackgroundColor *= (fCamSpaceZ > g_CameraAttribs.fFarPlaneZ) ? g_LightAttribs.f4ExtraterrestrialSunColor.rgb : 1; #if EXTINCTION_EVAL_MODE == EXTINCTION_EVAL_MODE_PER_PIXEL float3 f3ReconstructedPosWS = ProjSpaceXYZToWorldSpace(float3(In.m_f2PosPS.xy, fCamSpaceZ)); f3Extinction = GetExtinction(g_CameraAttribs.f4CameraPos.xyz, f3ReconstructedPosWS); #endif f3BackgroundColor *= f3Extinction; } #if PERFORM_TONE_MAPPING return ToneMap(f3BackgroundColor + f3Inscttering); #else const float DELTA = 0.00001; return log( max(DELTA, dot(f3BackgroundColor + f3Inscttering, RGB_TO_LUMINANCE)) ); #endif } technique11 ApplyInscatteredRadiance { pass PUnwarpInsctr { SetBlendState( NoBlending, float4( 0.0f, 0.0f, 0.0f, 0.0f ), 0xFFFFFFFF ); SetRasterizerState( RS_SolidFill_NoCull ); SetDepthStencilState( DSS_NoDepthTestIncrStencil, 0 ); SetVertexShader( CompileShader(vs_5_0, GenerateScreenSizeQuadVS() ) ); SetGeometryShader( NULL ); SetPixelShader( CompileShader(ps_5_0, ApplyInscatteredRadiancePS() ) ); } } struct PassThroughVS_Output { uint uiVertexID : VERTEX_ID; }; PassThroughVS_Output PassThroughVS(uint VertexID : SV_VertexID) { PassThroughVS_Output Out = {VertexID}; return Out; } struct SRenderSamplePositionsGS_Output { float4 f4PosPS : SV_Position; float3 f3Color : COLOR; float2 f2PosXY : XY; float4 f4QuadCenterAndSize : QUAD_CENTER_SIZE; }; [maxvertexcount(4)] void RenderSamplePositionsGS(point PassThroughVS_Output In[1], inout TriangleStream triStream ) { uint2 CoordTexDim; g_tex2DCoordinates.GetDimensions(CoordTexDim.x, CoordTexDim.y); uint2 TexelIJ = uint2( In[0].uiVertexID%CoordTexDim.x, In[0].uiVertexID/CoordTexDim.x ); float2 f2QuadCenterPos = g_tex2DCoordinates.Load(int3(TexelIJ,0)); uint2 ui2InterpolationSources = g_tex2DInterpolationSource.Load( uint3(TexelIJ,0) ); bool bIsInterpolation = ui2InterpolationSources.x != ui2InterpolationSources.y; float2 f2QuadSize = (bIsInterpolation ? 1.f : 4.f) / SCREEN_RESLOUTION.xy; float4 MinMaxUV = float4(f2QuadCenterPos.x-f2QuadSize.x, f2QuadCenterPos.y - f2QuadSize.y, f2QuadCenterPos.x+f2QuadSize.x, f2QuadCenterPos.y + f2QuadSize.y); float3 f3Color = bIsInterpolation ? float3(0.5,0,0) : float3(1,0,0); float4 Verts[4] = { float4(MinMaxUV.xy, 1.0, 1.0), float4(MinMaxUV.xw, 1.0, 1.0), float4(MinMaxUV.zy, 1.0, 1.0), float4(MinMaxUV.zw, 1.0, 1.0) }; for(int i=0; i<4; i++) { SRenderSamplePositionsGS_Output Out; Out.f4PosPS = Verts[i]; Out.f2PosXY = Out.f4PosPS.xy; Out.f3Color = f3Color; Out.f4QuadCenterAndSize = float4(f2QuadCenterPos, f2QuadSize); triStream.Append( Out ); } } float4 RenderSampleLocationsPS(SRenderSamplePositionsGS_Output In) : SV_Target { return float4(In.f3Color, 1 - pow( length( (In.f2PosXY - In.f4QuadCenterAndSize.xy) / In.f4QuadCenterAndSize.zw),4) ); } BlendState OverBS { BlendEnable[0] = TRUE; RenderTargetWriteMask[0] = 0x0F; BlendOp = ADD; SrcBlend = SRC_ALPHA; DestBlend = INV_SRC_ALPHA; SrcBlendAlpha = ZERO; DestBlendAlpha = INV_SRC_ALPHA; }; technique11 RenderSampleLocations { pass { SetBlendState( OverBS, float4( 0.0f, 0.0f, 0.0f, 0.0f ), 0xFFFFFFFF ); SetRasterizerState( RS_SolidFill_NoCull ); SetDepthStencilState( DSS_NoDepthTest, 0 ); SetVertexShader( CompileShader(vs_5_0, PassThroughVS() ) ); SetGeometryShader( CompileShader(gs_4_0, RenderSamplePositionsGS() ) ); SetPixelShader( CompileShader(ps_5_0, RenderSampleLocationsPS() ) ); } } float4 WorldParams2InsctrLUTCoords(float fHeight, float fCosViewZenithAngle, float fCosSunZenithAngle, float fCosSunViewAngle, in float4 f4PrevUVWQ = -1); float3 LookUpPrecomputedScattering(float3 f3StartPoint, float3 f3ViewDir, float3 f3EarthCentre, float3 f3DirOnLight, in Texture3D tex3DScatteringLUT, inout float4 f4UVWQ) { float3 f3EarthCentreToPointDir = f3StartPoint - f3EarthCentre; float fDistToEarthCentre = length(f3EarthCentreToPointDir); f3EarthCentreToPointDir /= fDistToEarthCentre; float fHeightAboveSurface = fDistToEarthCentre - EARTH_RADIUS; float fCosViewZenithAngle = dot( f3EarthCentreToPointDir, f3ViewDir ); float fCosSunZenithAngle = dot( f3EarthCentreToPointDir, f3DirOnLight ); float fCosSunViewAngle = dot( f3ViewDir, f3DirOnLight ); // Provide previous look-up coordinates f4UVWQ = WorldParams2InsctrLUTCoords(fHeightAboveSurface, fCosViewZenithAngle, fCosSunZenithAngle, fCosSunViewAngle, f4UVWQ); float3 f3UVW0; f3UVW0.xy = f4UVWQ.xy; float fQ0Slice = floor(f4UVWQ.w * PRECOMPUTED_SCTR_LUT_DIM.w - 0.5); fQ0Slice = clamp(fQ0Slice, 0, PRECOMPUTED_SCTR_LUT_DIM.w-1); float fQWeight = (f4UVWQ.w * PRECOMPUTED_SCTR_LUT_DIM.w - 0.5) - fQ0Slice; fQWeight = max(fQWeight, 0); float2 f2SliceMinMaxZ = float2(fQ0Slice, fQ0Slice+1)/PRECOMPUTED_SCTR_LUT_DIM.w + float2(0.5,-0.5) / (PRECOMPUTED_SCTR_LUT_DIM.z*PRECOMPUTED_SCTR_LUT_DIM.w); f3UVW0.z = (fQ0Slice + f4UVWQ.z) / PRECOMPUTED_SCTR_LUT_DIM.w; f3UVW0.z = clamp(f3UVW0.z, f2SliceMinMaxZ.x, f2SliceMinMaxZ.y); float fQ1Slice = min(fQ0Slice+1, PRECOMPUTED_SCTR_LUT_DIM.w-1); float fNextSliceOffset = (fQ1Slice - fQ0Slice) / PRECOMPUTED_SCTR_LUT_DIM.w; float3 f3UVW1 = f3UVW0 + float3(0,0,fNextSliceOffset); float3 f3Insctr0 = tex3DScatteringLUT.SampleLevel(samLinearClamp, f3UVW0, 0); float3 f3Insctr1 = tex3DScatteringLUT.SampleLevel(samLinearClamp, f3UVW1, 0); float3 f3Inscattering = lerp(f3Insctr0, f3Insctr1, fQWeight); return f3Inscattering; } float2 GetNetParticleDensity(in float fHeightAboveSurface, in float fCosZenithAngle) { float fRelativeHeightAboveSurface = fHeightAboveSurface / ATM_TOP_HEIGHT; return g_tex2DOccludedNetDensityToAtmTop.SampleLevel(samLinearClamp, float2(fRelativeHeightAboveSurface, fCosZenithAngle*0.5+0.5), 0).xy; } float2 GetNetParticleDensity(in float3 f3Pos, in float3 f3EarthCentre, in float3 f3RayDir) { float3 f3EarthCentreToPointDir = f3Pos - f3EarthCentre; float fDistToEarthCentre = length(f3EarthCentreToPointDir); f3EarthCentreToPointDir /= fDistToEarthCentre; float fHeightAboveSurface = fDistToEarthCentre - EARTH_RADIUS; float fCosZenithAngle = dot( f3EarthCentreToPointDir, f3RayDir ); return GetNetParticleDensity(fHeightAboveSurface, fCosZenithAngle); } void ApplyPhaseFunctions(inout float3 f3RayleighInscattering, inout float3 f3MieInscattering, in float cosTheta) { f3RayleighInscattering *= g_MediaParams.f4AngularRayleighSctrCoeff.rgb * (1.0 + cosTheta*cosTheta); // Apply Cornette-Shanks phase function (see Nishita et al. 93): // F(theta) = 1/(4*PI) * 3*(1-g^2) / (2*(2+g^2)) * (1+cos^2(theta)) / (1 + g^2 - 2g*cos(theta))^(3/2) // f4CS_g = ( 3*(1-g^2) / (2*(2+g^2)), 1+g^2, -2g, 1 ) float fDenom = rsqrt( dot(g_MediaParams.f4CS_g.yz, float2(1.f, cosTheta)) ); // 1 / (1 + g^2 - 2g*cos(theta))^(1/2) float fCornettePhaseFunc = g_MediaParams.f4CS_g.x * (fDenom*fDenom*fDenom) * (1 + cosTheta*cosTheta); f3MieInscattering *= g_MediaParams.f4AngularMieSctrCoeff.rgb * fCornettePhaseFunc; } // This function computes atmospheric properties in the given point void GetAtmosphereProperties(in float3 f3Pos, in float3 f3EarthCentre, in float3 f3DirOnLight, out float2 f2ParticleDensity, out float2 f2NetParticleDensityToAtmTop) { // Calculate the point height above the SPHERICAL Earth surface: float3 f3EarthCentreToPointDir = f3Pos - f3EarthCentre; float fDistToEarthCentre = length(f3EarthCentreToPointDir); f3EarthCentreToPointDir /= fDistToEarthCentre; float fHeightAboveSurface = fDistToEarthCentre - EARTH_RADIUS; f2ParticleDensity = exp( -fHeightAboveSurface / PARTICLE_SCALE_HEIGHT ); // Get net particle density from the integration point to the top of the atmosphere: float fCosSunZenithAngleForCurrPoint = dot( f3EarthCentreToPointDir, f3DirOnLight ); f2NetParticleDensityToAtmTop = GetNetParticleDensity(fHeightAboveSurface, fCosSunZenithAngleForCurrPoint); } // This function computes differential inscattering for the given particle densities // (without applying phase functions) void ComputePointDiffInsctr(in float2 f2ParticleDensityInCurrPoint, in float2 f2NetParticleDensityFromCam, in float2 f2NetParticleDensityToAtmTop, out float3 f3DRlghInsctr, out float3 f3DMieInsctr) { // Compute total particle density from the top of the atmosphere through the integraion point to camera float2 f2TotalParticleDensity = f2NetParticleDensityFromCam + f2NetParticleDensityToAtmTop; // Get optical depth float3 f3TotalRlghOpticalDepth = g_MediaParams.f4RayleighExtinctionCoeff.rgb * f2TotalParticleDensity.x; float3 f3TotalMieOpticalDepth = g_MediaParams.f4MieExtinctionCoeff.rgb * f2TotalParticleDensity.y; // And total extinction for the current integration point: float3 f3TotalExtinction = exp( -(f3TotalRlghOpticalDepth + f3TotalMieOpticalDepth) ); f3DRlghInsctr = f2ParticleDensityInCurrPoint.x * f3TotalExtinction; f3DMieInsctr = f2ParticleDensityInCurrPoint.y * f3TotalExtinction; } void ComputeInsctrIntegral(in float3 f3RayStart, in float3 f3RayEnd, in float3 f3EarthCentre, in float3 f3DirOnLight, inout float2 f2NetParticleDensityFromCam, inout float3 f3RayleighInscattering, inout float3 f3MieInscattering, uniform const float fNumSteps) { float3 f3Step = (f3RayEnd - f3RayStart) / fNumSteps; float fStepLen = length(f3Step); #if TRAPEZOIDAL_INTEGRATION // For trapezoidal integration we need to compute some variables for the starting point of the ray float2 f2PrevParticleDensity = 0; float2 f2NetParticleDensityToAtmTop = 0; GetAtmosphereProperties(f3RayStart, f3EarthCentre, f3DirOnLight, f2PrevParticleDensity, f2NetParticleDensityToAtmTop); float3 f3PrevDiffRInsctr = 0, f3PrevDiffMInsctr = 0; ComputePointDiffInsctr(f2PrevParticleDensity, f2NetParticleDensityFromCam, f2NetParticleDensityToAtmTop, f3PrevDiffRInsctr, f3PrevDiffMInsctr); #endif #if TRAPEZOIDAL_INTEGRATION // With trapezoidal integration, we will evaluate the function at the end of each section and // compute area of a trapezoid for(float fStepNum = 1.f; fStepNum <= fNumSteps; fStepNum += 1.f) #else // With stair-step integration, we will evaluate the function at the middle of each section and // compute area of a rectangle for(float fStepNum = 0.5f; fStepNum < fNumSteps; fStepNum += 1.f) #endif { float3 f3CurrPos = f3RayStart + f3Step * fStepNum; float2 f2ParticleDensity, f2NetParticleDensityToAtmTop; GetAtmosphereProperties(f3CurrPos, f3EarthCentre, f3DirOnLight, f2ParticleDensity, f2NetParticleDensityToAtmTop); // Accumulate net particle density from the camera to the integration point: #if TRAPEZOIDAL_INTEGRATION f2NetParticleDensityFromCam += (f2PrevParticleDensity + f2ParticleDensity) * (fStepLen / 2.f); f2PrevParticleDensity = f2ParticleDensity; #else f2NetParticleDensityFromCam += f2ParticleDensity * fStepLen; #endif float3 f3DRlghInsctr, f3DMieInsctr; ComputePointDiffInsctr(f2ParticleDensity, f2NetParticleDensityFromCam, f2NetParticleDensityToAtmTop, f3DRlghInsctr, f3DMieInsctr); #if TRAPEZOIDAL_INTEGRATION f3RayleighInscattering += (f3DRlghInsctr + f3PrevDiffRInsctr) * (fStepLen / 2.f); f3MieInscattering += (f3DMieInsctr + f3PrevDiffMInsctr) * (fStepLen / 2.f); f3PrevDiffRInsctr = f3DRlghInsctr; f3PrevDiffMInsctr = f3DMieInsctr; #else f3RayleighInscattering += f3DRlghInsctr * fStepLen; f3MieInscattering += f3DMieInsctr * fStepLen; #endif } } void IntegrateUnshadowedInscattering(in float3 f3RayStart, in float3 f3RayEnd, in float3 f3ViewDir, in float3 f3EarthCentre, in float3 f3DirOnLight, uniform const float fNumSteps, out float3 f3Inscattering, out float3 f3Extinction) { float2 f2NetParticleDensityFromCam = 0; float3 f3RayleighInscattering = 0; float3 f3MieInscattering = 0; ComputeInsctrIntegral( f3RayStart, f3RayEnd, f3EarthCentre, f3DirOnLight, f2NetParticleDensityFromCam, f3RayleighInscattering, f3MieInscattering, fNumSteps); float3 f3TotalRlghOpticalDepth = g_MediaParams.f4RayleighExtinctionCoeff.rgb * f2NetParticleDensityFromCam.x; float3 f3TotalMieOpticalDepth = g_MediaParams.f4MieExtinctionCoeff.rgb * f2NetParticleDensityFromCam.y; f3Extinction = exp( -(f3TotalRlghOpticalDepth + f3TotalMieOpticalDepth) ); // Apply phase function // Note that cosTheta = dot(DirOnCamera, LightDir) = dot(ViewDir, DirOnLight) because // DirOnCamera = -ViewDir and LightDir = -DirOnLight float cosTheta = dot(f3ViewDir, f3DirOnLight); ApplyPhaseFunctions(f3RayleighInscattering, f3MieInscattering, cosTheta); f3Inscattering = f3RayleighInscattering + f3MieInscattering; } void ComputeUnshadowedInscattering(float2 f2SampleLocation, float fCamSpaceZ, uniform const float fNumSteps, out float3 f3Inscattering, out float3 f3Extinction) { f3Inscattering = 0; f3Extinction = 1; float3 f3RayTermination = ProjSpaceXYZToWorldSpace( float3(f2SampleLocation, fCamSpaceZ) ); float3 f3CameraPos = g_CameraAttribs.f4CameraPos.xyz; float3 f3ViewDir = f3RayTermination - f3CameraPos; float fRayLength = length(f3ViewDir); f3ViewDir /= fRayLength; float3 f3EarthCentre = - float3(0,1,0) * EARTH_RADIUS; float2 f2RayAtmTopIsecs; GetRaySphereIntersection( f3CameraPos, f3ViewDir, f3EarthCentre, ATM_TOP_RADIUS, f2RayAtmTopIsecs); if( f2RayAtmTopIsecs.y <= 0 ) return; float3 f3RayStart = f3CameraPos + f3ViewDir * max(0, f2RayAtmTopIsecs.x); if( fCamSpaceZ > g_CameraAttribs.fFarPlaneZ ) // fFarPlaneZ is pre-multiplied with 0.999999f fRayLength = +FLT_MAX; float3 f3RayEnd = f3CameraPos + f3ViewDir * min(fRayLength, f2RayAtmTopIsecs.y); #if SINGLE_SCATTERING_MODE == SINGLE_SCTR_MODE_INTEGRATION IntegrateUnshadowedInscattering(f3RayStart, f3RayEnd, f3ViewDir, f3EarthCentre, g_LightAttribs.f4DirOnLight.xyz, fNumSteps, f3Inscattering, f3Extinction); #endif #if SINGLE_SCATTERING_MODE == SINGLE_SCTR_MODE_LUT || MULTIPLE_SCATTERING_MODE > MULTIPLE_SCTR_MODE_NONE #if MULTIPLE_SCATTERING_MODE > MULTIPLE_SCTR_MODE_NONE #if SINGLE_SCATTERING_MODE == SINGLE_SCTR_MODE_LUT Texture3D tex3DSctrLUT = g_tex3DMultipleSctrLUT; #elif SINGLE_SCATTERING_MODE == SINGLE_SCTR_MODE_NONE || SINGLE_SCATTERING_MODE == SINGLE_SCTR_MODE_INTEGRATION Texture3D tex3DSctrLUT = g_tex3DHighOrderSctrLUT; #endif #else Texture3D tex3DSctrLUT = g_tex3DSingleSctrLUT; #endif f3Extinction = GetExtinctionUnverified(f3RayStart, f3RayEnd, f3ViewDir, f3EarthCentre); // To avoid artifacts, we must be consistent when performing look-ups into the scattering texture, i.e. // we must assure that if the first look-up is above (below) horizon, then the second look-up // is also above (below) horizon. float4 f4UVWQ = -1; f3Inscattering += LookUpPrecomputedScattering(f3RayStart, f3ViewDir, f3EarthCentre, g_LightAttribs.f4DirOnLight.xyz, tex3DSctrLUT, f4UVWQ); // Provide previous look-up coordinates to the function to assure that look-ups are consistent f3Inscattering -= f3Extinction * LookUpPrecomputedScattering(f3RayEnd, f3ViewDir, f3EarthCentre, g_LightAttribs.f4DirOnLight.xyz, tex3DSctrLUT, f4UVWQ); #endif } // This function calculates inscattered light integral over the ray from the camera to // the specified world space position using ray marching float3 ComputeShadowedInscattering( in float2 f2RayMarchingSampleLocation, in float fRayEndCamSpaceZ, in float fCascadeInd, in uniform const bool bUse1DMinMaxMipMap = false, uint uiEpipolarSliceInd = 0 ) { float3 f3CameraPos = g_CameraAttribs.f4CameraPos.xyz; uint uiCascadeInd = fCascadeInd; // Compute the ray termination point, full ray length and view direction float3 f3RayTermination = ProjSpaceXYZToWorldSpace( float3(f2RayMarchingSampleLocation, fRayEndCamSpaceZ) ); float3 f3FullRay = f3RayTermination - f3CameraPos; float fFullRayLength = length(f3FullRay); float3 f3ViewDir = f3FullRay / fFullRayLength; const float3 f3EarthCentre = float3(0, -EARTH_RADIUS, 0); // Intersect the ray with the top of the atmosphere and the Earth: float4 f4Isecs; GetRaySphereIntersection2(f3CameraPos, f3ViewDir, f3EarthCentre, float2(ATM_TOP_RADIUS, EARTH_RADIUS), f4Isecs); float2 f2RayAtmTopIsecs = f4Isecs.xy; float2 f2RayEarthIsecs = f4Isecs.zw; if( f2RayAtmTopIsecs.y <= 0 ) { // view dir // / // d<0 / // *---------> * // . . . / . // . ' ' . . ' /\ ' . // / f2rayatmtopisecs.y < 0 // // the camera is outside the atmosphere and the ray either does not intersect the // top of it or the intersection point is behind the camera. In either // case there is no inscattering return 0; } // Restrict the camera position to the top of the atmosphere float fDistToAtmosphere = max(f2RayAtmTopIsecs.x, 0); float3 f3RestrainedCameraPos = f3CameraPos + fDistToAtmosphere * f3ViewDir; // Limit the ray length by the distance to the top of the atmosphere if the ray does not hit terrain float fOrigRayLength = fFullRayLength; if( fRayEndCamSpaceZ > g_CameraAttribs.fFarPlaneZ ) // fFarPlaneZ is pre-multiplied with 0.999999f fFullRayLength = +FLT_MAX; // Limit the ray length by the distance to the point where the ray exits the atmosphere fFullRayLength = min(fFullRayLength, f2RayAtmTopIsecs.y); // If there is an intersection with the Earth surface, limit the tracing distance to the intersection if( f2RayEarthIsecs.x > 0 ) { fFullRayLength = min(fFullRayLength, f2RayEarthIsecs.x); } fRayEndCamSpaceZ *= fFullRayLength / fOrigRayLength; float3 f3RayleighInscattering = 0; float3 f3MieInscattering = 0; float2 f2ParticleNetDensityFromCam = 0; float3 f3RayEnd = 0, f3RayStart = 0; // Note that cosTheta = dot(DirOnCamera, LightDir) = dot(ViewDir, DirOnLight) because // DirOnCamera = -ViewDir and LightDir = -DirOnLight float cosTheta = dot(f3ViewDir, g_LightAttribs.f4DirOnLight.xyz); float fCascadeEndCamSpaceZ = 0; float fTotalLitLength = 0, fTotalMarchedLength = 0; // Required for multiple scattering float fDistToFirstLitSection = -1; // Used only in when SINGLE_SCATTERING_MODE == SINGLE_SCTR_MODE_LUT #if CASCADE_PROCESSING_MODE == CASCADE_PROCESSING_MODE_SINGLE_PASS for(; uiCascadeInd < (uint)g_PPAttribs.m_iNumCascades; ++uiCascadeInd, ++fCascadeInd) #else for(int i=0; i<1; ++i) #endif { float2 f2CascadeStartEndCamSpaceZ = g_LightAttribs.ShadowAttribs.Cascades[uiCascadeInd].f4StartEndZ.xy; float fCascadeStartCamSpaceZ = f2CascadeStartEndCamSpaceZ.x;//(uiCascadeInd > (uint)g_PPAttribs.m_iFirstCascade) ? f2CascadeStartEndCamSpaceZ.x : 0; fCascadeEndCamSpaceZ = f2CascadeStartEndCamSpaceZ.y; // Check if the ray terminates before it enters current cascade if( fRayEndCamSpaceZ < fCascadeStartCamSpaceZ ) { #if CASCADE_PROCESSING_MODE == CASCADE_PROCESSING_MODE_SINGLE_PASS break; #else return 0; #endif } // Truncate the ray against the far and near planes of the current cascade: float fRayEndRatio = min( fRayEndCamSpaceZ, fCascadeEndCamSpaceZ ) / fRayEndCamSpaceZ; float fRayStartRatio = fCascadeStartCamSpaceZ / fRayEndCamSpaceZ; float fDistToRayStart = fFullRayLength * fRayStartRatio; float fDistToRayEnd = fFullRayLength * fRayEndRatio; // If the camera is outside the atmosphere and the ray intersects the top of it, // we must start integration from the first intersection point. // If the camera is in the atmosphere, first intersection point is always behind the camera // and thus is negative // // // // * / // . / . . / . // . ' /\ ' . . ' /\ ' . // / f2RayAtmTopIsecs.x > 0 / f2RayAtmTopIsecs.y > 0 // * // f2RayAtmTopIsecs.y > 0 f2RayAtmTopIsecs.x < 0 // / / // fDistToRayStart = max(fDistToRayStart, f2RayAtmTopIsecs.x); fDistToRayEnd = max(fDistToRayEnd, f2RayAtmTopIsecs.x); // To properly compute scattering from the space, we must // set up ray end position before extiting the loop f3RayEnd = f3CameraPos + f3ViewDir * fDistToRayEnd; f3RayStart = f3CameraPos + f3ViewDir * fDistToRayStart; #if CASCADE_PROCESSING_MODE != CASCADE_PROCESSING_MODE_SINGLE_PASS float r = length(f3RestrainedCameraPos - f3EarthCentre); float fCosZenithAngle = dot(f3RestrainedCameraPos-f3EarthCentre, f3ViewDir) / r; float fDist = max(fDistToRayStart - fDistToAtmosphere, 0); f2ParticleNetDensityFromCam = GetDensityIntegralAnalytic(r, fCosZenithAngle, fDist); #endif float fRayLength = fDistToRayEnd - fDistToRayStart; if( fRayLength <= 10 ) { #if CASCADE_PROCESSING_MODE == CASCADE_PROCESSING_MODE_SINGLE_PASS continue; #else if( (int)uiCascadeInd == g_PPAttribs.m_iNumCascades-1 ) // We need to process remaining part of the ray break; else return 0; #endif } // We trace the ray in the light projection space, not in the world space // Compute shadow map UV coordinates of the ray end point and its depth in the light space matrix mWorldToShadowMapUVDepth = g_LightAttribs.ShadowAttribs.mWorldToShadowMapUVDepth[uiCascadeInd]; float3 f3StartUVAndDepthInLightSpace = WorldSpaceToShadowMapUV(f3RayStart, mWorldToShadowMapUVDepth); //f3StartUVAndDepthInLightSpace.z -= SHADOW_MAP_DEPTH_BIAS; float3 f3EndUVAndDepthInLightSpace = WorldSpaceToShadowMapUV(f3RayEnd, mWorldToShadowMapUVDepth); //f3EndUVAndDepthInLightSpace.z -= SHADOW_MAP_DEPTH_BIAS; // Calculate normalized trace direction in the light projection space and its length float3 f3ShadowMapTraceDir = f3EndUVAndDepthInLightSpace.xyz - f3StartUVAndDepthInLightSpace.xyz; // If the ray is directed exactly at the light source, trace length will be zero // Clamp to a very small positive value to avoid division by zero float fTraceLenInShadowMapUVSpace = max( length( f3ShadowMapTraceDir.xy ), 1e-7 ); // Note that f3ShadowMapTraceDir.xy can be exactly zero f3ShadowMapTraceDir /= fTraceLenInShadowMapUVSpace; float fShadowMapUVStepLen = 0; float2 f2SliceOriginUV = 0; float2 f2SliceDirUV = 0; uint uiMinMaxTexYInd = 0; if( bUse1DMinMaxMipMap ) { // Get UV direction for this slice float4 f4SliceUVDirAndOrigin = g_tex2DSliceUVDirAndOrigin.Load( uint3(uiEpipolarSliceInd,uiCascadeInd,0) ); f2SliceDirUV = f4SliceUVDirAndOrigin.xy; //if( all(f4SliceUVDirAndOrigin == g_f4IncorrectSliceUVDirAndStart) ) //{ // return float3(0,0,0); //} //return float3(f4SliceUVDirAndOrigin.xy,0); // Scale with the shadow map texel size fShadowMapUVStepLen = length(f2SliceDirUV); f2SliceOriginUV = f4SliceUVDirAndOrigin.zw; #if USE_COMBINED_MIN_MAX_TEXTURE uiMinMaxTexYInd = uiEpipolarSliceInd + (uiCascadeInd - g_PPAttribs.m_iFirstCascade) * g_PPAttribs.m_uiNumEpipolarSlices; #else uiMinMaxTexYInd = uiEpipolarSliceInd; #endif } else { //Calculate length of the trace step in light projection space float fMaxTraceDirDim = max( abs(f3ShadowMapTraceDir.x), abs(f3ShadowMapTraceDir.y) ); fShadowMapUVStepLen = (fMaxTraceDirDim > 0) ? (g_PPAttribs.m_f2ShadowMapTexelSize.x / fMaxTraceDirDim) : 0; // Take into account maximum number of steps specified by the g_MiscParams.fMaxStepsAlongRay fShadowMapUVStepLen = max(fTraceLenInShadowMapUVSpace/g_MiscParams.fMaxStepsAlongRay, fShadowMapUVStepLen); } // Calcualte ray step length in world space float fRayStepLengthWS = fRayLength * (fShadowMapUVStepLen / fTraceLenInShadowMapUVSpace); // Note that fTraceLenInShadowMapUVSpace can be very small when looking directly at sun // Since fShadowMapUVStepLen is at least one shadow map texel in size, // fShadowMapUVStepLen / fTraceLenInShadowMapUVSpace >> 1 in this case and as a result // fRayStepLengthWS >> fRayLength // March the ray float fDistanceMarchedInCascade = 0; float3 f3CurrShadowMapUVAndDepthInLightSpace = f3StartUVAndDepthInLightSpace.xyz; // The following variables are used only if 1D min map optimization is enabled uint uiMinLevel = 0; // It is essential to round initial sample pos to the closest integer uint uiCurrSamplePos = length(f3StartUVAndDepthInLightSpace.xy - f2SliceOriginUV.xy)/fShadowMapUVStepLen + 0.5; uint uiCurrTreeLevel = 0; // Note that min/max shadow map does not contain finest resolution level // The first level it contains corresponds to step == 2 int iLevelDataOffset = -int(g_PPAttribs.m_uiMinMaxShadowMapResolution); float fStepScale = 1.f; float fMaxStepScale = g_PPAttribs.m_fMaxShadowMapStep; #if SINGLE_SCATTERING_MODE == SINGLE_SCTR_MODE_INTEGRATION // In order for the numerical integration to be accurate enough, it is necessary to make // at least 10 steps along the ray. To assure this, limit the maximum world step by // 1/10 of the ray length. // To avoid aliasing artifacts due to unstable sampling along the view ray, do this for // each cascade separately float fMaxAllowedWorldStepLen = fRayLength/10; fMaxStepScale = min(fMaxStepScale, fMaxAllowedWorldStepLen/fRayStepLengthWS); // Make sure that the world step length is not greater than the maximum allowable length if( fRayStepLengthWS > fMaxAllowedWorldStepLen ) { fRayStepLengthWS = fMaxAllowedWorldStepLen; // Recalculate shadow map UV step len fShadowMapUVStepLen = fTraceLenInShadowMapUVSpace * fRayStepLengthWS / fRayLength; // Disable 1D min/max optimization. Note that fMaxStepScale < 1 anyway since // fRayStepLengthWS > fMaxAllowedWorldStepLen. Thus there is no real need to // make the max shadow map step negative. We do this just for clarity fMaxStepScale = -1; } #endif // Scale trace direction in light projection space to calculate the step in shadow map float3 f3ShadowMapUVAndDepthStep = f3ShadowMapTraceDir * fShadowMapUVStepLen; [loop] while( fDistanceMarchedInCascade < fRayLength ) { // Clamp depth to a very small positive value to avoid z-fighting at camera location float fCurrDepthInLightSpace = max(f3CurrShadowMapUVAndDepthInLightSpace.z, 1e-7); float IsInLight = 0; if( bUse1DMinMaxMipMap ) { // If the step scale can be doubled without exceeding the maximum allowed scale and // the sample is located at the appropriate position, advance to the next coarser level if( 2*fStepScale < fMaxStepScale && ((uiCurrSamplePos & ((2<> uiCurrTreeLevel; uiCurrTreeLevel++; fStepScale *= 2.f; } while(uiCurrTreeLevel > uiMinLevel) { // Compute light space depths at the ends of the current ray section // What we need here is actually depth which is divided by the camera view space z // Thus depth can be correctly interpolated in screen space: // http://www.comp.nus.edu.sg/~lowkl/publications/lowk_persp_interp_techrep.pdf // A subtle moment here is that we need to be sure that we can skip fStepScale samples // starting from 0 up to fStepScale-1. We do not need to do any checks against the sample fStepScale away: // // ---------------> // // * // * * // * * // 0 1 2 3 // // |------------------>| // fStepScale = 4 float fNextLightSpaceDepth = f3CurrShadowMapUVAndDepthInLightSpace.z + f3ShadowMapUVAndDepthStep.z * (fStepScale-1); float2 f2StartEndDepthOnRaySection = float2(f3CurrShadowMapUVAndDepthInLightSpace.z, fNextLightSpaceDepth); f2StartEndDepthOnRaySection = f2StartEndDepthOnRaySection;//max(f2StartEndDepthOnRaySection, 1e-7); // Load 1D min/max depths float2 f2CurrMinMaxDepth = g_tex2DMinMaxLightSpaceDepth.Load( uint3( (uiCurrSamplePos>>uiCurrTreeLevel) + iLevelDataOffset, uiMinMaxTexYInd, 0) ); // Since we use complimentary depth buffer, the relations are reversed IsInLight = all( f2StartEndDepthOnRaySection >= f2CurrMinMaxDepth.yy ); bool bIsInShadow = all( f2StartEndDepthOnRaySection < f2CurrMinMaxDepth.xx ); if( IsInLight || bIsInShadow ) // If the ray section is fully lit or shadowed, we can break the loop break; // If the ray section is neither fully lit, nor shadowed, we have to go to the finer level uiCurrTreeLevel--; iLevelDataOffset -= (int)(g_PPAttribs.m_uiMinMaxShadowMapResolution >> uiCurrTreeLevel); fStepScale /= 2.f; }; // If we are at the finest level, sample the shadow map with PCF [branch] if( uiCurrTreeLevel <= uiMinLevel ) { IsInLight = g_tex2DLightSpaceDepthMap.SampleCmpLevelZero( samComparison, float3(f3CurrShadowMapUVAndDepthInLightSpace.xy,fCascadeInd), fCurrDepthInLightSpace ).x; } } else { IsInLight = g_tex2DLightSpaceDepthMap.SampleCmpLevelZero( samComparison, float3(f3CurrShadowMapUVAndDepthInLightSpace.xy,fCascadeInd), fCurrDepthInLightSpace ).x; } float fRemainingDist = max(fRayLength - fDistanceMarchedInCascade, 0); float fIntegrationStep = min(fRayStepLengthWS * fStepScale, fRemainingDist); float fIntegrationDist = fDistanceMarchedInCascade + fIntegrationStep/2; #if SINGLE_SCATTERING_MODE == SINGLE_SCTR_MODE_INTEGRATION float3 f3CurrPos = f3RayStart + f3ViewDir * fIntegrationDist; // Calculate integration point height above the SPHERICAL Earth surface: float3 f3EarthCentreToPointDir = f3CurrPos - f3EarthCentre; float fDistToEarthCentre = length(f3EarthCentreToPointDir); f3EarthCentreToPointDir /= fDistToEarthCentre; float fHeightAboveSurface = fDistToEarthCentre - EARTH_RADIUS; float2 f2ParticleDensity = exp( -fHeightAboveSurface / PARTICLE_SCALE_HEIGHT ); // Do not use this branch as it only degrades performance //if( IsInLight == 0) // continue; // Get net particle density from the integration point to the top of the atmosphere: float fCosSunZenithAngle = dot( f3EarthCentreToPointDir, g_LightAttribs.f4DirOnLight.xyz ); float2 f2NetParticleDensityToAtmTop = GetNetParticleDensity(fHeightAboveSurface, fCosSunZenithAngle); // Compute total particle density from the top of the atmosphere through the integraion point to camera float2 f2TotalParticleDensity = f2ParticleNetDensityFromCam + f2NetParticleDensityToAtmTop; // Update net particle density from the camera to the integration point: f2ParticleNetDensityFromCam += f2ParticleDensity * fIntegrationStep; // Get optical depth float3 f3TotalRlghOpticalDepth = g_MediaParams.f4RayleighExtinctionCoeff.rgb * f2TotalParticleDensity.x; float3 f3TotalMieOpticalDepth = g_MediaParams.f4MieExtinctionCoeff.rgb * f2TotalParticleDensity.y; // And total extinction for the current integration point: float3 f3TotalExtinction = exp( -(f3TotalRlghOpticalDepth + f3TotalMieOpticalDepth) ); f2ParticleDensity *= fIntegrationStep * IsInLight; f3RayleighInscattering += f2ParticleDensity.x * f3TotalExtinction; f3MieInscattering += f2ParticleDensity.y * f3TotalExtinction; #endif #if MULTIPLE_SCATTERING_MODE == MULTIPLE_SCTR_MODE_OCCLUDED || SINGLE_SCATTERING_MODE == SINGLE_SCTR_MODE_LUT // Store the distance where the ray first enters the light fDistToFirstLitSection = (fDistToFirstLitSection < 0 && IsInLight > 0) ? fTotalMarchedLength : fDistToFirstLitSection; #endif f3CurrShadowMapUVAndDepthInLightSpace += f3ShadowMapUVAndDepthStep * fStepScale; uiCurrSamplePos += 1 << uiCurrTreeLevel; // int -> float conversions are slow fDistanceMarchedInCascade += fRayStepLengthWS * fStepScale; #if MULTIPLE_SCATTERING_MODE == MULTIPLE_SCTR_MODE_OCCLUDED || SINGLE_SCATTERING_MODE == SINGLE_SCTR_MODE_LUT fTotalLitLength += fIntegrationStep * IsInLight; fTotalMarchedLength += fIntegrationStep; #endif } } #if MULTIPLE_SCATTERING_MODE == MULTIPLE_SCTR_MODE_OCCLUDED || SINGLE_SCATTERING_MODE == SINGLE_SCTR_MODE_LUT // If the whole ray is in shadow, set the distance to the first lit section to the // total marched distance if( fDistToFirstLitSection < 0 ) fDistToFirstLitSection = fTotalMarchedLength; #endif float3 f3RemainingRayStart = 0; float fRemainingLength = 0; if( #if CASCADE_PROCESSING_MODE != CASCADE_PROCESSING_MODE_SINGLE_PASS (int)uiCascadeInd == g_PPAttribs.m_iNumCascades-1 && #endif fRayEndCamSpaceZ > fCascadeEndCamSpaceZ ) { f3RemainingRayStart = f3RayEnd; f3RayEnd = f3CameraPos + fFullRayLength * f3ViewDir; fRemainingLength = length(f3RayEnd - f3RemainingRayStart); #if SINGLE_SCATTERING_MODE == SINGLE_SCTR_MODE_INTEGRATION // Do not allow integration step to become less than 50 km // Maximum possible view ray length is 2023 km (from the top of the // atmosphere touching the Earth and then again to the top of the // atmosphere). // For such ray, 41 integration step will be performed // Also assure that at least 20 steps are always performed float fMinStep = 50000.f; float fMumSteps = max(20, ceil(fRemainingLength/fMinStep) ); ComputeInsctrIntegral(f3RemainingRayStart, f3RayEnd, f3EarthCentre, g_LightAttribs.f4DirOnLight.xyz, f2ParticleNetDensityFromCam, f3RayleighInscattering, f3MieInscattering, fMumSteps); #endif } float3 f3InsctrIntegral = 0; #if SINGLE_SCATTERING_MODE == SINGLE_SCTR_MODE_INTEGRATION // Apply phase functions // Note that cosTheta = dot(DirOnCamera, LightDir) = dot(ViewDir, DirOnLight) because // DirOnCamera = -ViewDir and LightDir = -DirOnLight ApplyPhaseFunctions(f3RayleighInscattering, f3MieInscattering, cosTheta); f3InsctrIntegral = f3RayleighInscattering + f3MieInscattering; #endif #if CASCADE_PROCESSING_MODE == CASCADE_PROCESSING_MODE_SINGLE_PASS // Note that the first cascade used for ray marching must contain camera within it // otherwise this expression might fail f3RayStart = f3RestrainedCameraPos; #endif #if SINGLE_SCATTERING_MODE == SINGLE_SCTR_MODE_LUT || MULTIPLE_SCATTERING_MODE == MULTIPLE_SCTR_MODE_OCCLUDED #if MULTIPLE_SCATTERING_MODE == MULTIPLE_SCTR_MODE_OCCLUDED #if SINGLE_SCATTERING_MODE == SINGLE_SCTR_MODE_LUT Texture3D tex3DSctrLUT = g_tex3DMultipleSctrLUT; #elif SINGLE_SCATTERING_MODE == SINGLE_SCTR_MODE_NONE || SINGLE_SCATTERING_MODE == SINGLE_SCTR_MODE_INTEGRATION Texture3D tex3DSctrLUT = g_tex3DHighOrderSctrLUT; #endif #else Texture3D tex3DSctrLUT = g_tex3DSingleSctrLUT; #endif float3 f3MultipleScattering = 0; if( fTotalLitLength > 0 ) { float3 f3LitSectionStart = f3RayStart + fDistToFirstLitSection * f3ViewDir; float3 f3LitSectionEnd = f3LitSectionStart + fTotalLitLength * f3ViewDir; float3 f3ExtinctionToStart = GetExtinctionUnverified(f3RestrainedCameraPos, f3LitSectionStart, f3ViewDir, f3EarthCentre); float4 f4UVWQ = -1; f3MultipleScattering = f3ExtinctionToStart * LookUpPrecomputedScattering(f3LitSectionStart, f3ViewDir, f3EarthCentre, g_LightAttribs.f4DirOnLight.xyz, tex3DSctrLUT, f4UVWQ); float3 f3ExtinctionToEnd = GetExtinctionUnverified(f3RestrainedCameraPos, f3LitSectionEnd, f3ViewDir, f3EarthCentre); // To avoid artifacts, we must be consistent when performing look-ups into the scattering texture, i.e. // we must assure that if the first look-up is above (below) horizon, then the second look-up // is also above (below) horizon. // We provide previous look-up coordinates to the function so that it is able to figure out where the first look-up // was performed f3MultipleScattering -= f3ExtinctionToEnd * LookUpPrecomputedScattering(f3LitSectionEnd, f3ViewDir, f3EarthCentre, g_LightAttribs.f4DirOnLight.xyz, tex3DSctrLUT, f4UVWQ); f3InsctrIntegral += max(f3MultipleScattering, 0); } // Add contribution from the reminder of the ray behind the largest cascade if( fRemainingLength > 0 ) { float3 f3Extinction = GetExtinctionUnverified(f3RestrainedCameraPos, f3RemainingRayStart, f3ViewDir, f3EarthCentre); float4 f4UVWQ = -1; float3 f3RemainingInsctr = f3Extinction * LookUpPrecomputedScattering(f3RemainingRayStart, f3ViewDir, f3EarthCentre, g_LightAttribs.f4DirOnLight.xyz, tex3DSctrLUT, f4UVWQ); f3Extinction = GetExtinctionUnverified(f3RestrainedCameraPos, f3RayEnd, f3ViewDir, f3EarthCentre); f3RemainingInsctr -= f3Extinction * LookUpPrecomputedScattering(f3RayEnd, f3ViewDir, f3EarthCentre, g_LightAttribs.f4DirOnLight.xyz, tex3DSctrLUT, f4UVWQ); f3InsctrIntegral += max(f3RemainingInsctr, 0); } #endif #if MULTIPLE_SCATTERING_MODE == MULTIPLE_SCTR_MODE_UNOCCLUDED { float3 f3HighOrderScattering = 0, f3Extinction = 0; float4 f4UVWQ = -1; f3Extinction = GetExtinctionUnverified(f3RestrainedCameraPos, f3RayStart, f3ViewDir, f3EarthCentre); f3HighOrderScattering += f3Extinction * LookUpPrecomputedScattering(f3RayStart, f3ViewDir, f3EarthCentre, g_LightAttribs.f4DirOnLight.xyz, g_tex3DHighOrderSctrLUT, f4UVWQ); f3Extinction = GetExtinctionUnverified(f3RestrainedCameraPos, f3RayEnd, f3ViewDir, f3EarthCentre); // We provide previous look-up coordinates to the function so that it is able to figure out where the first look-up // was performed f3HighOrderScattering -= f3Extinction * LookUpPrecomputedScattering(f3RayEnd, f3ViewDir, f3EarthCentre, g_LightAttribs.f4DirOnLight.xyz, g_tex3DHighOrderSctrLUT, f4UVWQ); f3InsctrIntegral += f3HighOrderScattering; } #endif return f3InsctrIntegral * g_LightAttribs.f4ExtraterrestrialSunColor.rgb; } float3 RayMarchMinMaxOptPS(SScreenSizeQuadVSOutput In) : SV_TARGET { uint2 ui2SamplePosSliceInd = uint2(In.m_f4Pos.xy); float2 f2SampleLocation = g_tex2DCoordinates.Load( uint3(ui2SamplePosSliceInd, 0) ); float fRayEndCamSpaceZ = g_tex2DEpipolarCamSpaceZ.Load( uint3(ui2SamplePosSliceInd, 0) ); [branch] if( any(abs(f2SampleLocation) > 1+1e-3) ) return 0; float fCascade = g_MiscParams.fCascadeInd + In.m_fInstID; #if ENABLE_LIGHT_SHAFTS return ComputeShadowedInscattering(f2SampleLocation, fRayEndCamSpaceZ, fCascade, true, // Use min/max optimization ui2SamplePosSliceInd.y); #else float3 f3Inscattering, f3Extinction; ComputeUnshadowedInscattering(f2SampleLocation, fRayEndCamSpaceZ, g_PPAttribs.m_uiInstrIntegralSteps, f3Inscattering, f3Extinction); f3Inscattering *= g_LightAttribs.f4ExtraterrestrialSunColor.rgb; return f3Inscattering; #endif } float3 RayMarchPS(SScreenSizeQuadVSOutput In) : SV_TARGET { float2 f2SampleLocation = g_tex2DCoordinates.Load( uint3(In.m_f4Pos.xy, 0) ); float fRayEndCamSpaceZ = g_tex2DEpipolarCamSpaceZ.Load( uint3(In.m_f4Pos.xy, 0) ); [branch] if( any(abs(f2SampleLocation) > 1+1e-3) ) return 0; #if ENABLE_LIGHT_SHAFTS float fCascade = g_MiscParams.fCascadeInd + In.m_fInstID; return ComputeShadowedInscattering(f2SampleLocation, fRayEndCamSpaceZ, fCascade, false, // Do not use min/max optimization 0 // Ignored ); #else float3 f3Inscattering, f3Extinction; ComputeUnshadowedInscattering(f2SampleLocation, fRayEndCamSpaceZ, g_PPAttribs.m_uiInstrIntegralSteps, f3Inscattering, f3Extinction); f3Inscattering *= g_LightAttribs.f4ExtraterrestrialSunColor.rgb; return f3Inscattering; #endif } technique10 DoRayMarch { pass P0 { // Skip all samples which are not marked in the stencil as ray marching SetDepthStencilState( DSS_NoDepth_StEqual_IncrStencil, 2 ); SetRasterizerState( RS_SolidFill_NoCull ); SetBlendState( NoBlending, float4( 0.0f, 0.0f, 0.0f, 0.0f ), 0xFFFFFFFF ); SetVertexShader( CompileShader( vs_5_0, GenerateScreenSizeQuadVS() ) ); SetGeometryShader(NULL); SetPixelShader( CompileShader( ps_5_0, RayMarchPS() ) ); } pass P1 { // Skip all samples which are not marked in the stencil as ray marching SetDepthStencilState( DSS_NoDepth_StEqual_IncrStencil, 2 ); SetRasterizerState( RS_SolidFill_NoCull ); SetBlendState( NoBlending, float4( 0.0f, 0.0f, 0.0f, 0.0f ), 0xFFFFFFFF ); SetVertexShader( CompileShader( vs_5_0, GenerateScreenSizeQuadVS() ) ); SetGeometryShader(NULL); SetPixelShader( CompileShader( ps_5_0, RayMarchMinMaxOptPS() ) ); } } float3 FixInscatteredRadiancePS(SScreenSizeQuadVSOutput In) : SV_Target { if( g_PPAttribs.m_bShowDepthBreaks ) return float3(0,1,0); float fCascade = g_MiscParams.fCascadeInd + In.m_fInstID; float fRayEndCamSpaceZ = g_tex2DCamSpaceZ.SampleLevel( samLinearClamp, ProjToUV(In.m_f2PosPS.xy), 0 ); #if ENABLE_LIGHT_SHAFTS return ComputeShadowedInscattering(In.m_f2PosPS.xy, fRayEndCamSpaceZ, fCascade, false, // We cannot use min/max optimization at depth breaks 0 // Ignored ); #else float3 f3Inscattering, f3Extinction; ComputeUnshadowedInscattering(In.m_f2PosPS.xy, fRayEndCamSpaceZ, g_PPAttribs.m_uiInstrIntegralSteps, f3Inscattering, f3Extinction); f3Inscattering *= g_LightAttribs.f4ExtraterrestrialSunColor.rgb; return f3Inscattering; #endif } float3 FixAndApplyInscatteredRadiancePS(SScreenSizeQuadVSOutput In) : SV_Target { if( g_PPAttribs.m_bShowDepthBreaks ) return float3(0,1,0); float fCamSpaceZ = GetCamSpaceZ( ProjToUV(In.m_f2PosPS) ); float3 f3BackgroundColor = 0; [branch] if( !g_PPAttribs.m_bShowLightingOnly ) { f3BackgroundColor = g_tex2DColorBuffer.Load(int3(In.m_f4Pos.xy,0)).rgb; f3BackgroundColor *= (fCamSpaceZ > g_CameraAttribs.fFarPlaneZ) ? g_LightAttribs.f4ExtraterrestrialSunColor.rgb : 1; float3 f3ReconstructedPosWS = ProjSpaceXYZToWorldSpace(float3(In.m_f2PosPS.xy, fCamSpaceZ)); float3 f3Extinction = GetExtinction(g_CameraAttribs.f4CameraPos.xyz, f3ReconstructedPosWS); f3BackgroundColor *= f3Extinction.rgb; } float fCascade = g_MiscParams.fCascadeInd + In.m_fInstID; #if ENABLE_LIGHT_SHAFTS float3 f3InsctrColor = ComputeShadowedInscattering(In.m_f2PosPS.xy, fCamSpaceZ, fCascade, false, // We cannot use min/max optimization at depth breaks 0 // Ignored ); #else float3 f3InsctrColor, f3Extinction; ComputeUnshadowedInscattering(In.m_f2PosPS.xy, fCamSpaceZ, g_PPAttribs.m_uiInstrIntegralSteps, f3InsctrColor, f3Extinction); f3InsctrColor *= g_LightAttribs.f4ExtraterrestrialSunColor.rgb; #endif #if PERFORM_TONE_MAPPING return ToneMap(f3BackgroundColor + f3InsctrColor); #else const float DELTA = 0.00001; return log( max(DELTA, dot(f3BackgroundColor + f3InsctrColor, RGB_TO_LUMINANCE)) ); #endif } technique11 FixInscatteredRadiance { pass PRenderScatteringOnly { SetBlendState( NoBlending, float4( 0.0f, 0.0f, 0.0f, 0.0f ), 0xFFFFFFFF ); SetRasterizerState( RS_SolidFill_NoCull ); SetDepthStencilState( DSS_NoDepth_StEqual_IncrStencil, 0 ); SetVertexShader( CompileShader(vs_5_0, GenerateScreenSizeQuadVS() ) ); SetGeometryShader( NULL ); SetPixelShader( CompileShader(ps_5_0, FixAndApplyInscatteredRadiancePS() ) ); } } void RenderCoarseUnshadowedInsctrPS(SScreenSizeQuadVSOutput In, out float3 f3Inscattering : SV_Target0 #if EXTINCTION_EVAL_MODE == EXTINCTION_EVAL_MODE_EPIPOLAR , out float3 f3Extinction : SV_Target1 #endif ) { // Compute unshadowed inscattering from the camera to the ray end point using few steps float fCamSpaceZ = g_tex2DEpipolarCamSpaceZ.Load( uint3(In.m_f4Pos.xy, 0) ); float2 f2SampleLocation = g_tex2DCoordinates.Load( uint3(In.m_f4Pos.xy, 0) ); #if EXTINCTION_EVAL_MODE != EXTINCTION_EVAL_MODE_EPIPOLAR float3 f3Extinction = 1; #endif ComputeUnshadowedInscattering(f2SampleLocation, fCamSpaceZ, 7, // Use hard-coded constant here so that compiler can optimize the code // more efficiently f3Inscattering, f3Extinction); f3Inscattering *= g_LightAttribs.f4ExtraterrestrialSunColor.rgb; } technique11 RenderCoarseUnshadowedInsctr { pass PRenderScatteringOnly { SetBlendState( NoBlending, float4( 0.0f, 0.0f, 0.0f, 0.0f ), 0xFFFFFFFF ); SetRasterizerState( RS_SolidFill_NoCull ); SetDepthStencilState( DSS_NoDepth_StEqual_KeepStencil, 0 ); SetVertexShader( CompileShader(vs_5_0, GenerateScreenSizeQuadVS() ) ); SetGeometryShader( NULL ); SetPixelShader( CompileShader(ps_5_0, RenderCoarseUnshadowedInsctrPS() ) ); } } float2 IntegrateParticleDensity(in float3 f3Start, in float3 f3End, in float3 f3EarthCentre, float fNumSteps ) { float3 f3Step = (f3End - f3Start) / fNumSteps; float fStepLen = length(f3Step); float fStartHeightAboveSurface = abs( length(f3Start - f3EarthCentre) - g_MediaParams.fEarthRadius ); float2 f2PrevParticleDensity = exp( -fStartHeightAboveSurface / g_MediaParams.f2ParticleScaleHeight ); float2 f2ParticleNetDensity = 0; for(float fStepNum = 1; fStepNum <= fNumSteps; fStepNum += 1.f) { float3 f3CurrPos = f3Start + f3Step * fStepNum; float fHeightAboveSurface = abs( length(f3CurrPos - f3EarthCentre) - g_MediaParams.fEarthRadius ); float2 f2ParticleDensity = exp( -fHeightAboveSurface / g_MediaParams.f2ParticleScaleHeight ); f2ParticleNetDensity += (f2ParticleDensity + f2PrevParticleDensity) * fStepLen / 2.f; f2PrevParticleDensity = f2ParticleDensity; } return f2ParticleNetDensity; } float2 IntegrateParticleDensityAlongRay(in float3 f3Pos, in float3 f3RayDir, float3 f3EarthCentre, uniform const float fNumSteps, uniform const bool bOccludeByEarth) { if( bOccludeByEarth ) { // If the ray intersects the Earth, return huge optical depth float2 f2RayEarthIsecs; GetRaySphereIntersection(f3Pos, f3RayDir, f3EarthCentre, g_MediaParams.fEarthRadius, f2RayEarthIsecs); if( f2RayEarthIsecs.x > 0 ) return 1e+20; } // Get intersection with the top of the atmosphere (the start point must always be under the top of it) // // / // . / . // . ' /\ ' . // / f2RayAtmTopIsecs.y > 0 // * // f2RayAtmTopIsecs.x < 0 // / // float2 f2RayAtmTopIsecs; GetRaySphereIntersection(f3Pos, f3RayDir, f3EarthCentre, g_MediaParams.fAtmTopRadius, f2RayAtmTopIsecs); float fIntegrationDist = f2RayAtmTopIsecs.y; float3 f3RayEnd = f3Pos + f3RayDir * fIntegrationDist; return IntegrateParticleDensity(f3Pos, f3RayEnd, f3EarthCentre, fNumSteps); } float2 PrecomputeNetDensityToAtmTopPS( SScreenSizeQuadVSOutput In ) : SV_Target0 { float2 f2UV = ProjToUV(In.m_f2PosPS); // Do not allow start point be at the Earth surface and on the top of the atmosphere float fStartHeight = clamp( lerp(0, g_MediaParams.fAtmTopHeight, f2UV.x), 10, g_MediaParams.fAtmTopHeight-10 ); float fCosTheta = -In.m_f2PosPS.y; float fSinTheta = sqrt( saturate(1 - fCosTheta*fCosTheta) ); float3 f3RayStart = float3(0, 0, fStartHeight); float3 f3RayDir = float3(fSinTheta, 0, fCosTheta); float3 f3EarthCentre = float3(0,0,-g_MediaParams.fEarthRadius); const float fNumSteps = 200; return IntegrateParticleDensityAlongRay(f3RayStart, f3RayDir, f3EarthCentre, fNumSteps, true); } technique11 PrecomputeNetDensityToAtmTopTech { pass P0 { SetBlendState( NoBlending, float4( 0.0f, 0.0f, 0.0f, 0.0f ), 0xFFFFFFFF ); SetRasterizerState( RS_SolidFill_NoCull ); SetDepthStencilState( DSS_NoDepthTest, 0 ); SetVertexShader( CompileShader(vs_5_0, GenerateScreenSizeQuadVS() ) ); SetGeometryShader( NULL ); SetPixelShader( CompileShader(ps_5_0, PrecomputeNetDensityToAtmTopPS() ) ); } } // This function for analytical evaluation of particle density integral is // provided by Eric Bruneton // http://www-evasion.inrialpes.fr/Membres/Eric.Bruneton/ // // optical depth for ray (r,mu) of length d, using analytic formula // (mu=cos(view zenith angle)), intersections with ground ignored float2 GetDensityIntegralAnalytic(float r, float mu, float d) { float2 f2A = sqrt( (0.5/PARTICLE_SCALE_HEIGHT.xy) * r ); float4 f4A01 = f2A.xxyy * float2(mu, mu + d / r).xyxy; float4 f4A01s = sign(f4A01); float4 f4A01sq = f4A01*f4A01; float2 f2X; f2X.x = f4A01s.y > f4A01s.x ? exp(f4A01sq.x) : 0.0; f2X.y = f4A01s.w > f4A01s.z ? exp(f4A01sq.z) : 0.0; float4 f4Y = f4A01s / (2.3193*abs(f4A01) + sqrt(1.52*f4A01sq + 4.0)) * float3(1.0, exp(-d/PARTICLE_SCALE_HEIGHT.xy*(d/(2.0*r)+mu))).xyxz; return sqrt((6.2831*PARTICLE_SCALE_HEIGHT)*r) * exp((EARTH_RADIUS-r)/PARTICLE_SCALE_HEIGHT.xy) * (f2X + float2( dot(f4Y.xy, float2(1.0, -1.0)), dot(f4Y.zw, float2(1.0, -1.0)) )); } float3 GetExtinctionUnverified(in float3 f3StartPos, in float3 f3EndPos, float3 f3EyeDir, float3 f3EarthCentre) { #if 0 float2 f2ParticleDensity = IntegrateParticleDensity(f3StartPos, f3EndPos, f3EarthCentre, 20); #else float r = length(f3StartPos-f3EarthCentre); float fCosZenithAngle = dot(f3StartPos-f3EarthCentre, f3EyeDir) / r; float2 f2ParticleDensity = GetDensityIntegralAnalytic(r, fCosZenithAngle, length(f3StartPos - f3EndPos)); #endif // Get optical depth float3 f3TotalRlghOpticalDepth = g_MediaParams.f4RayleighExtinctionCoeff.rgb * f2ParticleDensity.x; float3 f3TotalMieOpticalDepth = g_MediaParams.f4MieExtinctionCoeff.rgb * f2ParticleDensity.y; // Compute extinction float3 f3Extinction = exp( -(f3TotalRlghOpticalDepth + f3TotalMieOpticalDepth) ); return f3Extinction; } float3 GetExtinction(in float3 f3StartPos, in float3 f3EndPos) { float3 f3EyeDir = f3EndPos - f3StartPos; float fRayLength = length(f3EyeDir); f3EyeDir /= fRayLength; float3 f3EarthCentre = /*g_CameraAttribs.f4CameraPos.xyz*float3(1,0,1)*/ - float3(0,1,0) * EARTH_RADIUS; float2 f2RayAtmTopIsecs; // Compute intersections of the view ray with the atmosphere GetRaySphereIntersection(f3StartPos, f3EyeDir, f3EarthCentre, ATM_TOP_RADIUS, f2RayAtmTopIsecs); // If the ray misses the atmosphere, there is no extinction if( f2RayAtmTopIsecs.y < 0 )return 1; // Do not let the start and end point be outside the atmosphere f3EndPos = f3StartPos + f3EyeDir * min(f2RayAtmTopIsecs.y, fRayLength); f3StartPos += f3EyeDir * max(f2RayAtmTopIsecs.x, 0); return GetExtinctionUnverified(f3StartPos, f3EndPos, f3EyeDir, f3EarthCentre); } float GetCosHorizonAnlge(float fHeight) { // Due to numeric precision issues, fHeight might sometimes be slightly negative fHeight = max(fHeight, 0); return -sqrt(fHeight * (2*EARTH_RADIUS + fHeight) ) / (EARTH_RADIUS + fHeight); } float ZenithAngle2TexCoord(float fCosZenithAngle, float fHeight, in float fTexDim, float power, float fPrevTexCoord) { fCosZenithAngle = fCosZenithAngle; float fTexCoord; float fCosHorzAngle = GetCosHorizonAnlge(fHeight); // When performing look-ups into the scattering texture, it is very important that all the look-ups are consistent // wrt to the horizon. This means that if the first look-up is above (below) horizon, then the second look-up // should also be above (below) horizon. // We use previous texture coordinate, if it is provided, to find out if previous look-up was above or below // horizon. If texture coordinate is negative, then this is the first look-up bool bIsAboveHorizon = fPrevTexCoord >= 0.5; bool bIsBelowHorizon = 0 <= fPrevTexCoord && fPrevTexCoord < 0.5; if( bIsAboveHorizon || !bIsBelowHorizon && (fCosZenithAngle > fCosHorzAngle) ) { // Scale to [0,1] fTexCoord = saturate( (fCosZenithAngle - fCosHorzAngle) / (1 - fCosHorzAngle) ); fTexCoord = pow(fTexCoord, power); // Now remap texture coordinate to the upper half of the texture. // To avoid filtering across discontinuity at 0.5, we must map // the texture coordinate to [0.5 + 0.5/fTexDim, 1 - 0.5/fTexDim] // // 0.5 1.5 D/2+0.5 D-0.5 texture coordinate x dimension // | | | | // | X | X | .... | X || X | .... | X | // 0 1 D/2-1 D/2 D-1 texel index // fTexCoord = 0.5f + 0.5f / fTexDim + fTexCoord * (fTexDim/2 - 1) / fTexDim; } else { fTexCoord = saturate( (fCosHorzAngle - fCosZenithAngle) / (fCosHorzAngle - (-1)) ); fTexCoord = pow(fTexCoord, power); // Now remap texture coordinate to the lower half of the texture. // To avoid filtering across discontinuity at 0.5, we must map // the texture coordinate to [0.5, 0.5 - 0.5/fTexDim] // // 0.5 1.5 D/2-0.5 texture coordinate x dimension // | | | // | X | X | .... | X || X | .... // 0 1 D/2-1 D/2 texel index // fTexCoord = 0.5f / fTexDim + fTexCoord * (fTexDim/2 - 1) / fTexDim; } return fTexCoord; } float TexCoord2ZenithAngle(float fTexCoord, float fHeight, in float fTexDim, float power) { float fCosZenithAngle; float fCosHorzAngle = GetCosHorizonAnlge(fHeight); if( fTexCoord > 0.5 ) { // Remap to [0,1] from the upper half of the texture [0.5 + 0.5/fTexDim, 1 - 0.5/fTexDim] fTexCoord = saturate( (fTexCoord - (0.5f + 0.5f / fTexDim)) * fTexDim / (fTexDim/2 - 1) ); fTexCoord = pow(fTexCoord, 1/power); // Assure that the ray does NOT hit Earth fCosZenithAngle = max( (fCosHorzAngle + fTexCoord * (1 - fCosHorzAngle)), fCosHorzAngle + 1e-4); } else { // Remap to [0,1] from the lower half of the texture [0.5, 0.5 - 0.5/fTexDim] fTexCoord = saturate((fTexCoord - 0.5f / fTexDim) * fTexDim / (fTexDim/2 - 1)); fTexCoord = pow(fTexCoord, 1/power); // Assure that the ray DOES hit Earth fCosZenithAngle = min( (fCosHorzAngle - fTexCoord * (fCosHorzAngle - (-1))), fCosHorzAngle - 1e-4); } return fCosZenithAngle; } static const float SafetyHeightMargin = 16.f; #define NON_LINEAR_PARAMETERIZATION 1 static const float HeightPower = 0.5f; static const float ViewZenithPower = 0.2; static const float SunViewPower = 1.5f; void InsctrLUTCoords2WorldParams(in float4 f4UVWQ, out float fHeight, out float fCosViewZenithAngle, out float fCosSunZenithAngle, out float fCosSunViewAngle) { #if NON_LINEAR_PARAMETERIZATION // Rescale to exactly 0,1 range f4UVWQ.xzw = saturate((f4UVWQ* PRECOMPUTED_SCTR_LUT_DIM - 0.5) / (PRECOMPUTED_SCTR_LUT_DIM-1)).xzw; f4UVWQ.x = pow( f4UVWQ.x, 1/HeightPower ); // Allowable height range is limited to [SafetyHeightMargin, AtmTopHeight - SafetyHeightMargin] to // avoid numeric issues at the Earth surface and the top of the atmosphere fHeight = f4UVWQ.x * (g_MediaParams.fAtmTopHeight - 2*SafetyHeightMargin) + SafetyHeightMargin; fCosViewZenithAngle = TexCoord2ZenithAngle(f4UVWQ.y, fHeight, PRECOMPUTED_SCTR_LUT_DIM.y, ViewZenithPower); // Use Eric Bruneton's formula for cosine of the sun-zenith angle fCosSunZenithAngle = tan((2.0 * f4UVWQ.z - 1.0 + 0.26) * 1.1) / tan(1.26 * 1.1); f4UVWQ.w = sign(f4UVWQ.w - 0.5) * pow( abs((f4UVWQ.w - 0.5)*2), 1/SunViewPower)/2 + 0.5; fCosSunViewAngle = cos(f4UVWQ.w*PI); #else // Rescale to exactly 0,1 range f4UVWQ = (f4UVWQ * PRECOMPUTED_SCTR_LUT_DIM - 0.5) / (PRECOMPUTED_SCTR_LUT_DIM-1); // Allowable height range is limited to [SafetyHeightMargin, AtmTopHeight - SafetyHeightMargin] to // avoid numeric issues at the Earth surface and the top of the atmosphere fHeight = f4UVWQ.x * (g_MediaParams.fAtmTopHeight - 2*SafetyHeightMargin) + SafetyHeightMargin; fCosViewZenithAngle = f4UVWQ.y * 2 - 1; fCosSunZenithAngle = f4UVWQ.z * 2 - 1; fCosSunViewAngle = f4UVWQ.w * 2 - 1; #endif fCosViewZenithAngle = clamp(fCosViewZenithAngle, -1, +1); fCosSunZenithAngle = clamp(fCosSunZenithAngle, -1, +1); // Compute allowable range for the cosine of the sun view angle for the given // view zenith and sun zenith angles float D = (1.0 - fCosViewZenithAngle * fCosViewZenithAngle) * (1.0 - fCosSunZenithAngle * fCosSunZenithAngle); // !!!! IMPORTANT NOTE regarding NVIDIA hardware !!!! // There is a very weird issue on NVIDIA hardware with clamp(), saturate() and min()/max() // functions. No matter what function is used, fCosViewZenithAngle and fCosSunZenithAngle // can slightly fall outside [-1,+1] range causing D to be negative // Using saturate(D), max(D, 0) and even D>0?D:0 does not work! // The only way to avoid taking the square root of negative value and obtaining NaN is // to use max() with small positive value: D = sqrt( max(D, 1e-20) ); // The issue was reproduceable on NV GTX 680, driver version 9.18.13.2723 (9/12/2013). // The problem does not arise on Intel hardware float2 f2MinMaxCosSunViewAngle = fCosViewZenithAngle*fCosSunZenithAngle + float2(-D, +D); // Clamp to allowable range fCosSunViewAngle = clamp(fCosSunViewAngle, f2MinMaxCosSunViewAngle.x, f2MinMaxCosSunViewAngle.y); } float4 WorldParams2InsctrLUTCoords(float fHeight, float fCosViewZenithAngle, float fCosSunZenithAngle, float fCosSunViewAngle, in float4 f4RefUVWQ) { float4 f4UVWQ; // Limit allowable height range to [SafetyHeightMargin, AtmTopHeight - SafetyHeightMargin] to // avoid numeric issues at the Earth surface and the top of the atmosphere // (ray/Earth and ray/top of the atmosphere intersection tests are unstable when fHeight == 0 and // fHeight == AtmTopHeight respectively) fHeight = clamp(fHeight, SafetyHeightMargin, g_MediaParams.fAtmTopHeight - SafetyHeightMargin); f4UVWQ.x = saturate( (fHeight - SafetyHeightMargin) / (g_MediaParams.fAtmTopHeight - 2*SafetyHeightMargin) ); #if NON_LINEAR_PARAMETERIZATION f4UVWQ.x = pow(f4UVWQ.x, HeightPower); f4UVWQ.y = ZenithAngle2TexCoord(fCosViewZenithAngle, fHeight, PRECOMPUTED_SCTR_LUT_DIM.y, ViewZenithPower, f4RefUVWQ.y); // Use Eric Bruneton's formula for cosine of the sun-zenith angle f4UVWQ.z = (atan(max(fCosSunZenithAngle, -0.1975) * tan(1.26 * 1.1)) / 1.1 + (1.0 - 0.26)) * 0.5; fCosSunViewAngle = clamp(fCosSunViewAngle, -1, +1); f4UVWQ.w = acos(fCosSunViewAngle) / PI; f4UVWQ.w = sign(f4UVWQ.w - 0.5) * pow( abs((f4UVWQ.w - 0.5)/0.5), SunViewPower)/2 + 0.5; f4UVWQ.xzw = ((f4UVWQ * (PRECOMPUTED_SCTR_LUT_DIM-1) + 0.5) / PRECOMPUTED_SCTR_LUT_DIM).xzw; #else f4UVWQ.y = (fCosViewZenithAngle+1.f) / 2.f; f4UVWQ.z = (fCosSunZenithAngle +1.f) / 2.f; f4UVWQ.w = (fCosSunViewAngle +1.f) / 2.f; f4UVWQ = (f4UVWQ * (PRECOMPUTED_SCTR_LUT_DIM-1) + 0.5) / PRECOMPUTED_SCTR_LUT_DIM; #endif return f4UVWQ; } float3 ComputeViewDir(in float fCosViewZenithAngle) { return float3(sqrt(saturate(1 - fCosViewZenithAngle*fCosViewZenithAngle)), fCosViewZenithAngle, 0); } float3 ComputeLightDir(in float3 f3ViewDir, in float fCosSunZenithAngle, in float fCosSunViewAngle) { float3 f3DirOnLight; f3DirOnLight.x = (f3ViewDir.x > 0) ? (fCosSunViewAngle - fCosSunZenithAngle * f3ViewDir.y) / f3ViewDir.x : 0; f3DirOnLight.y = fCosSunZenithAngle; f3DirOnLight.z = sqrt( saturate(1 - dot(f3DirOnLight.xy, f3DirOnLight.xy)) ); // Do not normalize f3DirOnLight! Even if its length is not exactly 1 (which can // happen because of fp precision issues), all the dot products will still be as // specified, which is essentially important. If we normalize the vector, all the // dot products will deviate, resulting in wrong pre-computation. // Since fCosSunViewAngle is clamped to allowable range, f3DirOnLight should always // be normalized. However, due to some issues on NVidia hardware sometimes // it may not be as that (see IMPORTANT NOTE regarding NVIDIA hardware) //f3DirOnLight = normalize(f3DirOnLight); return f3DirOnLight; } // This shader pre-computes the radiance of single scattering at a given point in given // direction. float3 PrecomputeSingleScatteringPS(SScreenSizeQuadVSOutput In) : SV_Target { // Get attributes for the current point float2 f2UV = ProjToUV(In.m_f2PosPS); float fHeight, fCosViewZenithAngle, fCosSunZenithAngle, fCosSunViewAngle; InsctrLUTCoords2WorldParams(float4(f2UV, g_MiscParams.f2WQ), fHeight, fCosViewZenithAngle, fCosSunZenithAngle, fCosSunViewAngle ); float3 f3EarthCentre = - float3(0,1,0) * EARTH_RADIUS; float3 f3RayStart = float3(0, fHeight, 0); float3 f3ViewDir = ComputeViewDir(fCosViewZenithAngle); float3 f3DirOnLight = ComputeLightDir(f3ViewDir, fCosSunZenithAngle, fCosSunViewAngle); // Intersect view ray with the top of the atmosphere and the Earth float4 f4Isecs; GetRaySphereIntersection2( f3RayStart, f3ViewDir, f3EarthCentre, float2(EARTH_RADIUS, ATM_TOP_RADIUS), f4Isecs); float2 f2RayEarthIsecs = f4Isecs.xy; float2 f2RayAtmTopIsecs = f4Isecs.zw; if(f2RayAtmTopIsecs.y <= 0) return 0; // This is just a sanity check and should never happen // as the start point is always under the top of the // atmosphere (look at InsctrLUTCoords2WorldParams()) // Set the ray length to the distance to the top of the atmosphere float fRayLength = f2RayAtmTopIsecs.y; // If ray hits Earth, limit the length by the distance to the surface if(f2RayEarthIsecs.x > 0) fRayLength = min(fRayLength, f2RayEarthIsecs.x); float3 f3RayEnd = f3RayStart + f3ViewDir * fRayLength; // Integrate single-scattering float3 f3Inscattering, f3Extinction; IntegrateUnshadowedInscattering(f3RayStart, f3RayEnd, f3ViewDir, f3EarthCentre, f3DirOnLight.xyz, 100, f3Inscattering, f3Extinction); return f3Inscattering; } // This shader pre-computes the radiance of light scattered at a given point in given // direction. It multiplies the previous order in-scattered light with the phase function // for each type of particles and integrates the result over the whole set of directions, // see eq. (7) in [Bruneton and Neyret 08]. float3 ComputeSctrRadiancePS(SScreenSizeQuadVSOutput In) : SV_Target { // Get attributes for the current point float2 f2UV = ProjToUV(In.m_f2PosPS); float fHeight, fCosViewZenithAngle, fCosSunZenithAngle, fCosSunViewAngle; InsctrLUTCoords2WorldParams( float4(f2UV, g_MiscParams.f2WQ), fHeight, fCosViewZenithAngle, fCosSunZenithAngle, fCosSunViewAngle ); float3 f3EarthCentre = - float3(0,1,0) * EARTH_RADIUS; float3 f3RayStart = float3(0, fHeight, 0); float3 f3ViewDir = ComputeViewDir(fCosViewZenithAngle); float3 f3DirOnLight = ComputeLightDir(f3ViewDir, fCosSunZenithAngle, fCosSunViewAngle); // Compute particle density scale factor float2 f2ParticleDensity = exp( -fHeight / PARTICLE_SCALE_HEIGHT ); float3 f3SctrRadiance = 0; // Go through a number of samples randomly distributed over the sphere for(int iSample = 0; iSample < NUM_RANDOM_SPHERE_SAMPLES; ++iSample) { // Get random direction float3 f3RandomDir = normalize( g_tex2DSphereRandomSampling.Load(int3(iSample,0,0)) ); // Get the previous order in-scattered light when looking in direction f3RandomDir (the light thus goes in direction -f3RandomDir) float4 f4UVWQ = -1; float3 f3PrevOrderSctr = LookUpPrecomputedScattering(f3RayStart, f3RandomDir, f3EarthCentre, f3DirOnLight.xyz, g_tex3DPreviousSctrOrder, f4UVWQ); // Apply phase functions for each type of particles // Note that total scattering coefficients are baked into the angular scattering coeffs float3 f3DRlghInsctr = f2ParticleDensity.x * f3PrevOrderSctr; float3 f3DMieInsctr = f2ParticleDensity.y * f3PrevOrderSctr; float fCosTheta = dot(f3ViewDir, f3RandomDir); ApplyPhaseFunctions(f3DRlghInsctr, f3DMieInsctr, fCosTheta); f3SctrRadiance += f3DRlghInsctr + f3DMieInsctr; } // Since we tested N random samples, each sample covered 4*Pi / N solid angle // Note that our phase function is normalized to 1 over the sphere. For instance, // uniform phase function would be p(theta) = 1 / (4*Pi). // Notice that for uniform intensity I if we get N samples, we must obtain exactly I after // numeric integration return f3SctrRadiance * 4*PI / NUM_RANDOM_SPHERE_SAMPLES; } // This shader computes in-scattering order for a given point and direction. It performs integration of the // light scattered at particular point along the ray, see eq. (11) in [Bruneton and Neyret 08]. float3 ComputeScatteringOrderPS(SScreenSizeQuadVSOutput In) : SV_Target { // Get attributes for the current point float2 f2UV = ProjToUV(In.m_f2PosPS); float fHeight, fCosViewZenithAngle, fCosSunZenithAngle, fCosSunViewAngle; InsctrLUTCoords2WorldParams(float4(f2UV, g_MiscParams.f2WQ), fHeight, fCosViewZenithAngle, fCosSunZenithAngle, fCosSunViewAngle ); float3 f3EarthCentre = - float3(0,1,0) * EARTH_RADIUS; float3 f3RayStart = float3(0, fHeight, 0); float3 f3ViewDir = ComputeViewDir(fCosViewZenithAngle); float3 f3DirOnLight = ComputeLightDir(f3ViewDir, fCosSunZenithAngle, fCosSunViewAngle); // Intersect the ray with the atmosphere and Earth float4 f4Isecs; GetRaySphereIntersection2( f3RayStart, f3ViewDir, f3EarthCentre, float2(EARTH_RADIUS, ATM_TOP_RADIUS), f4Isecs); float2 f2RayEarthIsecs = f4Isecs.xy; float2 f2RayAtmTopIsecs = f4Isecs.zw; if(f2RayAtmTopIsecs.y <= 0) return 0; // This is just a sanity check and should never happen // as the start point is always under the top of the // atmosphere (look at InsctrLUTCoords2WorldParams()) float fRayLength = f2RayAtmTopIsecs.y; if(f2RayEarthIsecs.x > 0) fRayLength = min(fRayLength, f2RayEarthIsecs.x); float3 f3RayEnd = f3RayStart + f3ViewDir * fRayLength; const float fNumSamples = 64; float fStepLen = fRayLength / fNumSamples; float4 f4UVWQ = -1; float3 f3PrevSctrRadiance = LookUpPrecomputedScattering(f3RayStart, f3ViewDir, f3EarthCentre, f3DirOnLight.xyz, g_tex3DPointwiseSctrRadiance, f4UVWQ); float2 f2PrevParticleDensity = exp( -fHeight / PARTICLE_SCALE_HEIGHT ); float2 f2NetParticleDensityFromCam = 0; float3 f3Inscattering = 0; for(float fSample=1; fSample <= fNumSamples; ++fSample) { float3 f3Pos = lerp(f3RayStart, f3RayEnd, fSample/fNumSamples); float fCurrHeight = length(f3Pos - f3EarthCentre) - EARTH_RADIUS; float2 f2ParticleDensity = exp( -fCurrHeight / PARTICLE_SCALE_HEIGHT ); f2NetParticleDensityFromCam += (f2PrevParticleDensity + f2ParticleDensity) * (fStepLen / 2.f); f2PrevParticleDensity = f2ParticleDensity; // Get optical depth float3 f3RlghOpticalDepth = g_MediaParams.f4RayleighExtinctionCoeff.rgb * f2NetParticleDensityFromCam.x; float3 f3MieOpticalDepth = g_MediaParams.f4MieExtinctionCoeff.rgb * f2NetParticleDensityFromCam.y; // Compute extinction from the camera for the current integration point: float3 f3ExtinctionFromCam = exp( -(f3RlghOpticalDepth + f3MieOpticalDepth) ); // Get attenuated scattered light radiance in the current point float4 f4UVWQ = -1; float3 f3SctrRadiance = f3ExtinctionFromCam * LookUpPrecomputedScattering(f3Pos, f3ViewDir, f3EarthCentre, f3DirOnLight.xyz, g_tex3DPointwiseSctrRadiance, f4UVWQ); // Update in-scattering integral f3Inscattering += (f3SctrRadiance + f3PrevSctrRadiance) * (fStepLen/2.f); f3PrevSctrRadiance = f3SctrRadiance; } return f3Inscattering; } float3 AddScatteringOrderPS(SScreenSizeQuadVSOutput In) : SV_Target { // Accumulate in-scattering using alpha-blending return g_tex3DPreviousSctrOrder.Load( uint4(In.m_f4Pos.xy, g_MiscParams.uiDepthSlice, 0) ); } technique11 PrecomputeScatteringTech { pass P0 { SetBlendState( NoBlending, float4( 0.0f, 0.0f, 0.0f, 0.0f ), 0xFFFFFFFF ); SetRasterizerState( RS_SolidFill_NoCull ); SetDepthStencilState( DSS_NoDepthTest, 0 ); SetVertexShader( CompileShader(vs_5_0, GenerateScreenSizeQuadVS() ) ); SetGeometryShader( NULL ); SetPixelShader( CompileShader(ps_5_0, PrecomputeSingleScatteringPS() ) ); } pass P1 { SetBlendState( NoBlending, float4( 0.0f, 0.0f, 0.0f, 0.0f ), 0xFFFFFFFF ); SetRasterizerState( RS_SolidFill_NoCull ); SetDepthStencilState( DSS_NoDepthTest, 0 ); SetVertexShader( CompileShader(vs_5_0, GenerateScreenSizeQuadVS() ) ); SetGeometryShader( NULL ); SetPixelShader( CompileShader(ps_5_0, ComputeSctrRadiancePS() ) ); } pass P2 { SetBlendState( NoBlending, float4( 0.0f, 0.0f, 0.0f, 0.0f ), 0xFFFFFFFF ); SetRasterizerState( RS_SolidFill_NoCull ); SetDepthStencilState( DSS_NoDepthTest, 0 ); SetVertexShader( CompileShader(vs_5_0, GenerateScreenSizeQuadVS() ) ); SetGeometryShader( NULL ); SetPixelShader( CompileShader(ps_5_0, ComputeScatteringOrderPS() ) ); } } float3 PrecomputeAmbientSkyLightPS(SScreenSizeQuadVSOutput In) : SV_Target { float fU = ProjToUV(In.m_f2PosPS).x; float3 f3RayStart = float3(0,20,0); float3 f3EarthCentre = -float3(0,1,0) * EARTH_RADIUS; float fCosZenithAngle = clamp(fU * 2 - 1, -1, +1); float3 f3DirOnLight = float3(sqrt(saturate(1 - fCosZenithAngle*fCosZenithAngle)), fCosZenithAngle, 0); float3 f3SkyLight = 0; // Go through a number of random directions on the sphere for(int iSample = 0; iSample < NUM_RANDOM_SPHERE_SAMPLES; ++iSample) { // Get random direction float3 f3RandomDir = normalize( g_tex2DSphereRandomSampling.Load(int3(iSample,0,0)) ); // Reflect directions from the lower hemisphere f3RandomDir.y = abs(f3RandomDir.y); // Get multiple scattered light radiance when looking in direction f3RandomDir (the light thus goes in direction -f3RandomDir) float4 f4UVWQ = -1; float3 f3Sctr = LookUpPrecomputedScattering(f3RayStart, f3RandomDir, f3EarthCentre, f3DirOnLight.xyz, g_tex3DPreviousSctrOrder, f4UVWQ); // Accumulate ambient irradiance through the horizontal plane f3SkyLight += f3Sctr * dot(f3RandomDir, float3(0,1,0)); } // Each sample covers 2 * PI / NUM_RANDOM_SPHERE_SAMPLES solid angle (integration is performed over // upper hemisphere) return f3SkyLight * 2 * PI / NUM_RANDOM_SPHERE_SAMPLES; } struct SSunVSOutput { float4 m_f4Pos : SV_Position; float2 m_f2PosPS : PosPS; // Position in projection space [-1,1]x[-1,1] }; static const float fSunAngularRadius = 32.f/2.f / 60.f * ((2.f * PI)/180); // Sun angular DIAMETER is 32 arc minutes static const float fTanSunAngularRadius = tan(fSunAngularRadius); SSunVSOutput SunVS(in uint VertexId : SV_VertexID) { float2 fCotanHalfFOV = float2( g_CameraAttribs.mProj[0][0], g_CameraAttribs.mProj[1][1] ); float2 f2SunScreenPos = g_LightAttribs.f4LightScreenPos.xy; float2 f2SunScreenSize = fTanSunAngularRadius * fCotanHalfFOV; float4 MinMaxUV = f2SunScreenPos.xyxy + float4(-1,-1,1,1) * f2SunScreenSize.xyxy; SSunVSOutput Verts[4] = { {float4(MinMaxUV.xy, 0.0, 1.0), MinMaxUV.xy}, {float4(MinMaxUV.xw, 0.0, 1.0), MinMaxUV.xw}, {float4(MinMaxUV.zy, 0.0, 1.0), MinMaxUV.zy}, {float4(MinMaxUV.zw, 0.0, 1.0), MinMaxUV.zw} }; return Verts[VertexId]; } float3 SunPS(SSunVSOutput In) : SV_Target { float2 fCotanHalfFOV = float2( g_CameraAttribs.mProj[0][0], g_CameraAttribs.mProj[1][1] ); float2 f2SunScreenSize = fTanSunAngularRadius * fCotanHalfFOV; float2 f2dXY = (In.m_f2PosPS - g_LightAttribs.f4LightScreenPos.xy) / f2SunScreenSize; return sqrt(saturate(1 - dot(f2dXY, f2dXY))); } technique11 RenderSunTech { pass P0 { SetBlendState( NoBlending, float4( 0.0f, 0.0f, 0.0f, 0.0f ), 0xFFFFFFFF ); SetRasterizerState( RS_SolidFill_NoCull ); SetVertexShader( CompileShader(vs_5_0, SunVS() ) ); SetGeometryShader( NULL ); SetPixelShader( CompileShader(ps_5_0, SunPS() ) ); } } ================================================ FILE: fx/RefineSampleLocations.fx ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #include "Common.fxh" RWTexture2D g_rwtex2DInterpolationSource : register( u0 ); #ifndef INITIAL_SAMPLE_STEP # define INITIAL_SAMPLE_STEP 128 #endif #ifndef THREAD_GROUP_SIZE # define THREAD_GROUP_SIZE max(INITIAL_SAMPLE_STEP, 32) #endif #ifndef REFINEMENT_CRITERION # define REFINEMENT_CRITERION REFINEMENT_CRITERION_INSCTR_DIFF #endif // In my first implementation I used group shared memory to store camera space z // values. This was a very low-performing method // After that I tried using arrays of bool flags instead, but this did not help very much // since memory bandwidth was almost the same (on GPU each bool consumes 4 bytes) // Finally, I came up with packing 32 flags into single uint value. // This not only enables using 32x times less memory, but also enables very efficient // test if depth break is present in the section static const uint g_uiNumPackedFlags = THREAD_GROUP_SIZE/32; groupshared uint g_uiPackedCamSpaceDiffFlags[ g_uiNumPackedFlags ]; #if REFINEMENT_CRITERION == REFINEMENT_CRITERION_INSCTR_DIFF groupshared float3 g_f3Inscattering[THREAD_GROUP_SIZE+1]; #endif [numthreads(THREAD_GROUP_SIZE, 1, 1)] void RefineSampleLocationsCS(uint3 Gid : SV_GroupID, uint3 GTid : SV_GroupThreadID) { // Each thread group processes one slice uint uiSliceInd = Gid.y; // Compute global index of the first sample in the thread group // Each group processes THREAD_GROUP_SIZE samples in the slice uint uiGroupStartGlobalInd = Gid.x * THREAD_GROUP_SIZE; uint uiSampleInd = GTid.x; // Sample index in the group // Compute global index of this sample which is required to fetch the sample's coordinates uint uiGlobalSampleInd = uiGroupStartGlobalInd + uiSampleInd; // Load location of the current sample using global sample index float2 f2SampleLocationPS = g_tex2DCoordinates.Load( uint3(uiGlobalSampleInd, uiSliceInd, 0) ); bool bIsValidThread = all( abs(f2SampleLocationPS) < 1+1e-4 ); // Initialize flags with zeroes if( GTid.x < g_uiNumPackedFlags ) g_uiPackedCamSpaceDiffFlags[GTid.x] = 0; GroupMemoryBarrierWithGroupSync(); // Let each thread in the group compute its own flag // Note that if the sample is located behind the screen, its flag will be set to zero // Besides, since g_tex2DEpipolarCamSpaceZ is cleared with invalid coordinates, the difference // flag between valid and invalid locations will also be zero. Thus the sample next to invalid will always // be marked as ray marching sample [branch] if( bIsValidThread ) { #if REFINEMENT_CRITERION == REFINEMENT_CRITERION_DEPTH_DIFF // Load camera space Z for this sample and for its right neighbour (remeber to use global sample index) float fCamSpaceZ = g_tex2DEpipolarCamSpaceZ.Load( uint3(uiGlobalSampleInd, uiSliceInd, 0) ); float fRightNeighbCamSpaceZ = g_tex2DEpipolarCamSpaceZ.Load( uint3(uiGlobalSampleInd+1, uiSliceInd, 0) ); float fMaxZ = max(fCamSpaceZ, fRightNeighbCamSpaceZ); fMaxZ = max(fMaxZ, 1); // Compare the difference with the threshold bool bFlag = abs(fCamSpaceZ - fRightNeighbCamSpaceZ)/fMaxZ < 0.2*g_PPAttribs.m_fRefinementThreshold; #elif REFINEMENT_CRITERION == REFINEMENT_CRITERION_INSCTR_DIFF // Load inscattering for this sample and for its right neighbour float3 f3Insctr0 = g_tex2DScatteredColor.Load( uint3(uiGlobalSampleInd, uiSliceInd, 0) ); float3 f3Insctr1 = g_tex2DScatteredColor.Load( uint3(uiGlobalSampleInd+1, uiSliceInd, 0) ); float3 f3MaxInsctr = max(f3Insctr0, f3Insctr1); // Compute minimum inscattering threshold based on the average scene luminance float fAverageLum = GetAverageSceneLuminance(); // Inscattering threshold should be proportional to the average scene luminance and // inversely proportional to the middle gray level (the higher middle gray, the briter the scene, // thus the less the theshold) // It should also account for the fact that rgb channels contribute differently // to the percieved brightness. For r channel the threshold should be smallest, // for b channel - the largest float3 f3MinInsctrThreshold = (0.02f * fAverageLum.xxx / RGB_TO_LUMINANCE.xyz) / g_PPAttribs.m_fMiddleGray; f3MaxInsctr = max(f3MaxInsctr, f3MinInsctrThreshold); // Compare the difference with the threshold. If the neighbour sample is invalid, its inscattering // is large negative value and the difference is guaranteed to be larger than the threshold bool bFlag = all( (abs(f3Insctr0 - f3Insctr1)/f3MaxInsctr) < g_PPAttribs.m_fRefinementThreshold ); #endif // Set appropriate flag using INTERLOCKED Or: InterlockedOr( g_uiPackedCamSpaceDiffFlags[uiSampleInd/32], bFlag << (uiSampleInd%32) ); } // Synchronize threads in the group GroupMemoryBarrierWithGroupSync(); // Skip invalid threads. This can be done only after the synchronization if( !bIsValidThread ) return; // uiInitialSampleStep // uiSampleInd |<--------->| // | | | // X * * * X * * * X * * * X X - locations of initial samples // | | // | uiInitialSample1Ind // uiInitialSample0Ind // // Find two closest initial ray marching samples uint uiInitialSampleStep = INITIAL_SAMPLE_STEP; uint uiInitialSample0Ind = (uiSampleInd / uiInitialSampleStep) * uiInitialSampleStep; // Use denser sampling near the epipole to account for high variation // Note that sampling near the epipole is very cheap since only a few steps // are required to perform ray marching uint uiInitialSample0GlobalInd = uiInitialSample0Ind + uiGroupStartGlobalInd; float2 f2InitialSample0Coords = g_tex2DCoordinates.Load( uint3(uiInitialSample0GlobalInd, uiSliceInd, 0) ); if( uiInitialSample0GlobalInd/(float)MAX_SAMPLES_IN_SLICE < 0.05 && length(f2InitialSample0Coords - g_LightAttribs.f4LightScreenPos.xy) < 0.1 ) { uiInitialSampleStep = max( INITIAL_SAMPLE_STEP / g_PPAttribs.m_uiEpipoleSamplingDensityFactor, 1 ); uiInitialSample0Ind = (uiSampleInd / uiInitialSampleStep) * uiInitialSampleStep; } uint uiInitialSample1Ind = uiInitialSample0Ind + uiInitialSampleStep; // Remeber that the last sample in each epipolar slice must be ray marching one uint uiInterpolationTexWidth, uiInterpolationTexHeight; g_rwtex2DInterpolationSource.GetDimensions(uiInterpolationTexWidth, uiInterpolationTexHeight); if( Gid.x == uiInterpolationTexWidth/THREAD_GROUP_SIZE - 1 ) uiInitialSample1Ind = min(uiInitialSample1Ind, THREAD_GROUP_SIZE-1); uint uiLeftSrcSampleInd = uiSampleInd; uint uiRightSrcSampleInd = uiSampleInd; // Do nothing if sample is one of initial samples. In this case the sample will be // interpolated from itself if( uiSampleInd > uiInitialSample0Ind && uiSampleInd < uiInitialSample1Ind ) { // Load group shared memory to the thread local memory uint uiPackedCamSpaceDiffFlags[ g_uiNumPackedFlags ]; for(uint i=0; i < g_uiNumPackedFlags; ++i) uiPackedCamSpaceDiffFlags[i] = g_uiPackedCamSpaceDiffFlags[i]; // Check if there are no depth breaks in the whole section // In such case all the flags are set bool bNoDepthBreaks = true; #if INITIAL_SAMPLE_STEP < 32 { // Check if all uiInitialSampleStep flags starting from // position uiInitialSample0Ind are set: int iFlagPackOrder = uiInitialSample0Ind / 32; int iFlagOrderInPack = uiInitialSample0Ind % 32; uint uiFlagPack = uiPackedCamSpaceDiffFlags[iFlagPackOrder]; uint uiAllFlagsMask = ((1<> iFlagOrderInPack) & uiAllFlagsMask) != uiAllFlagsMask ) bNoDepthBreaks = false; } #else { for(uint i=0; i < g_uiNumPackedFlags; ++i) if( uiPackedCamSpaceDiffFlags[i] != 0xFFFFFFFFU ) // If at least one flag is not set, there is a depth break on this section bNoDepthBreaks = false; } #endif if( bNoDepthBreaks ) { // If there are no depth breaks, we can skip all calculations // and use initial sample locations as interpolation sources: uiLeftSrcSampleInd = uiInitialSample0Ind; uiRightSrcSampleInd = uiInitialSample1Ind; } else { // Find left interpolation source { // Note that i-th flag reflects the difference between i-th and (i+1)-th samples: // Flag[i] = abs(fCamSpaceZ[i] - fCamSpaceZ[i+1]) < g_PPAttribs.m_fRefinementThreshold; // We need to find first depth break starting from iFirstDepthBreakToTheLeftInd sample // and going to the left up to uiInitialSample0Ind int iFirstDepthBreakToTheLeftInd = uiSampleInd-1; // iFirstDepthBreakToTheLeftInd // | // V // 0 1 2 3 30 31 32 33 .... i-1 i i+1 .... 63 64 // | | 1 1 1 1 | // uiPackedCamSpaceDiffFlags[0] uiPackedCamSpaceDiffFlags[1] // // iFlagOrderInPack == i % 32 int iFlagPackOrder = uint(iFirstDepthBreakToTheLeftInd) / 32; int iFlagOrderInPack = uint(iFirstDepthBreakToTheLeftInd) % 32; uint uiFlagPack = uiPackedCamSpaceDiffFlags[iFlagPackOrder]; // To test if there is a depth break in the current flag pack, // we must check all flags starting from the iFlagOrderInPack // downward to 0 position. We must skip all flags from iFlagOrderInPack+1 to 31 if( iFlagOrderInPack < 31 ) { // Set all higher flags to 1, so that they will be skipped // Note that if iFlagOrderInPack == 31, there are no flags to skip // Note also that (U << 32) != 0 as it can be expected. (U << 32) == U instead uiFlagPack |= ( uint(0x0FFFFFFFFU) << uint(iFlagOrderInPack+1) ); } // Find first zero flag starting from iFlagOrderInPack position. Since all // higher bits are set, they will be effectivelly skipped int iFirstUnsetFlagPos = firstbithigh( uint(~uiFlagPack) ); // firstbithigh(0) == +INT_MAX if( !(0 <= iFirstUnsetFlagPos && iFirstUnsetFlagPos < 32) ) // There are no set flags => proceed to the next uint flag pack iFirstUnsetFlagPos = -1; iFirstDepthBreakToTheLeftInd -= iFlagOrderInPack - iFirstUnsetFlagPos; #if INITIAL_SAMPLE_STEP > 32 // Check the remaining full flag packs iFlagPackOrder--; while( iFlagPackOrder >= 0 && iFirstUnsetFlagPos == -1 ) { uiFlagPack = uiPackedCamSpaceDiffFlags[iFlagPackOrder]; iFirstUnsetFlagPos = firstbithigh( uint(~uiFlagPack) ); if( !(0 <= iFirstUnsetFlagPos && iFirstUnsetFlagPos < 32) ) iFirstUnsetFlagPos = -1; iFirstDepthBreakToTheLeftInd -= 31 - iFirstUnsetFlagPos; iFlagPackOrder--; } #endif // Ray marching sample is located next to the identified depth break: uiLeftSrcSampleInd = max( uint(iFirstDepthBreakToTheLeftInd + 1), uiInitialSample0Ind ); } // Find right interpolation source using symmetric method { // We need to find first depth break starting from iRightSrcSampleInd and // going to the right up to the uiInitialSample1Ind uiRightSrcSampleInd = uiSampleInd; int iFlagPackOrder = uiRightSrcSampleInd / 32; uint iFlagOrderInPack = uiRightSrcSampleInd % 32; uint uiFlagPack = uiPackedCamSpaceDiffFlags[iFlagPackOrder]; // We need to find first unset flag in the current flag pack // starting from iFlagOrderInPack position and up to the 31st bit // Set all lower order bits to 1 so that they are skipped during // the test: if( iFlagOrderInPack > 0 ) uiFlagPack |= ( (1 << uint(iFlagOrderInPack))-1 ); // Find first zero flag: int iFirstUnsetFlagPos = firstbitlow( uint(~uiFlagPack) ); if( !(0 <= iFirstUnsetFlagPos && iFirstUnsetFlagPos < 32) ) iFirstUnsetFlagPos = 32; uiRightSrcSampleInd += iFirstUnsetFlagPos - iFlagOrderInPack; #if INITIAL_SAMPLE_STEP > 32 // Check the remaining full flag packs iFlagPackOrder++; while( iFlagPackOrder < int(g_uiNumPackedFlags) && iFirstUnsetFlagPos == 32 ) { uiFlagPack = uiPackedCamSpaceDiffFlags[iFlagPackOrder]; iFirstUnsetFlagPos = firstbitlow( uint(~uiFlagPack) ); if( !(0 <= iFirstUnsetFlagPos && iFirstUnsetFlagPos < 32) ) iFirstUnsetFlagPos = 32; uiRightSrcSampleInd += iFirstUnsetFlagPos; iFlagPackOrder++; } #endif uiRightSrcSampleInd = min(uiRightSrcSampleInd, uiInitialSample1Ind); } } // If at least one interpolation source is the same as the sample itself, the // sample is ray marching sample and is interpolated from itself: if(uiLeftSrcSampleInd == uiSampleInd || uiRightSrcSampleInd == uiSampleInd ) uiLeftSrcSampleInd = uiRightSrcSampleInd = uiSampleInd; } g_rwtex2DInterpolationSource[ uint2(uiGlobalSampleInd, uiSliceInd) ] = uint2(uiGroupStartGlobalInd + uiLeftSrcSampleInd, uiGroupStartGlobalInd + uiRightSrcSampleInd); } technique11 RefineSampleLocations { pass { SetVertexShader( NULL ); SetGeometryShader( NULL ); SetPixelShader( NULL ); SetComputeShader( CompileShader(cs_5_0, RefineSampleLocationsCS() ) ); } } ================================================ FILE: fx/Structures.fxh ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #ifndef _STRCUTURES_FXH_ #define _STRCUTURES_FXH_ #define PI 3.1415928f #ifdef __cplusplus # define float2 D3DXVECTOR2 # define float3 D3DXVECTOR3 # define float4 D3DXVECTOR4 # define uint UINT #else # define BOOL bool // Do not use bool, because sizeof(bool)==1 ! #endif #ifdef __cplusplus # define CHECK_STRUCT_ALIGNMENT(s) static_assert( sizeof(s) % 16 == 0, "sizeof("#s") is not multiple of 16" ); #else # define CHECK_STRUCT_ALIGNMENT(s) #endif #define MAX_CASCADES 8 struct SCascadeAttribs { float4 f4LightSpaceScale; float4 f4LightSpaceScaledBias; float4 f4StartEndZ; }; #ifdef __cplusplus static_assert( (sizeof(SCascadeAttribs) % 16) == 0, "sizeof(SCascadeAttribs) is not multiple of 16" ); #endif struct SShadowMapAttribs { // 0 #ifdef __cplusplus D3DXMATRIX mWorldToLightViewT; // Matrices in HLSL are COLUMN-major while D3DXMATRIX is ROW major #else matrix mWorldToLightView; // Transform from view space to light projection space #endif // 16 SCascadeAttribs Cascades[MAX_CASCADES]; #ifdef __cplusplus float fCascadeCamSpaceZEnd[MAX_CASCADES]; D3DXMATRIX mWorldToShadowMapUVDepthT[MAX_CASCADES]; #else float4 f4CascadeCamSpaceZEnd[MAX_CASCADES/4]; matrix mWorldToShadowMapUVDepth[MAX_CASCADES]; #endif // Do not use bool, because sizeof(bool)==1 ! BOOL bVisualizeCascades; float3 f3Padding; }; #ifdef __cplusplus static_assert( (sizeof(SShadowMapAttribs) % 16) == 0, "sizeof(SShadowMapAttribs) is not multiple of 16" ); #endif struct SLightAttribs { float4 f4DirOnLight; float4 f4AmbientLight; float4 f4LightScreenPos; float4 f4ExtraterrestrialSunColor; BOOL bIsLightOnScreen; float3 f3Dummy; SShadowMapAttribs ShadowAttribs; }; CHECK_STRUCT_ALIGNMENT(SLightAttribs); struct SCameraAttribs { float4 f4CameraPos; ///< Camera world position float fNearPlaneZ; float fFarPlaneZ; // fNearPlaneZ < fFarPlaneZ float2 f2Dummy; #ifdef __cplusplus D3DXMATRIX WorldViewProjT; D3DXMATRIX mViewT; D3DXMATRIX mProjT; D3DXMATRIX mViewProjInvT; #else matrix WorldViewProj; matrix mView; matrix mProj; matrix mViewProjInv; #endif }; CHECK_STRUCT_ALIGNMENT(SCameraAttribs); #define LIGHT_SCTR_TECHNIQUE_EPIPOLAR_SAMPLING 0 #define LIGHT_SCTR_TECHNIQUE_BRUTE_FORCE 1 #define CASCADE_PROCESSING_MODE_SINGLE_PASS 0 #define CASCADE_PROCESSING_MODE_MULTI_PASS 1 #define CASCADE_PROCESSING_MODE_MULTI_PASS_INST 2 #define REFINEMENT_CRITERION_DEPTH_DIFF 0 #define REFINEMENT_CRITERION_INSCTR_DIFF 1 // Extinction evaluation mode used when attenuating background #define EXTINCTION_EVAL_MODE_PER_PIXEL 0// Evaluate extinction for each pixel using analytic formula // by Eric Bruneton #define EXTINCTION_EVAL_MODE_EPIPOLAR 1 // Render extinction in epipolar space and perform // bilateral filtering in the same manner as for // inscattering #define SINGLE_SCTR_MODE_NONE 0 #define SINGLE_SCTR_MODE_INTEGRATION 1 #define SINGLE_SCTR_MODE_LUT 2 #define MULTIPLE_SCTR_MODE_NONE 0 #define MULTIPLE_SCTR_MODE_UNOCCLUDED 1 #define MULTIPLE_SCTR_MODE_OCCLUDED 2 #define TONE_MAPPING_MODE_EXP 0 #define TONE_MAPPING_MODE_REINHARD 1 #define TONE_MAPPING_MODE_REINHARD_MOD 2 #define TONE_MAPPING_MODE_UNCHARTED2 3 #define TONE_MAPPING_FILMIC_ALU 4 #define TONE_MAPPING_LOGARITHMIC 5 #define TONE_MAPPING_ADAPTIVE_LOG 6 struct SPostProcessingAttribs { uint m_uiNumEpipolarSlices; uint m_uiMaxSamplesInSlice; uint m_uiInitialSampleStepInSlice; uint m_uiEpipoleSamplingDensityFactor; float m_fRefinementThreshold; // do not use bool, because sizeof(bool)==1 and as a result bool variables // will be incorrectly mapped on GPU constant buffer BOOL m_bShowSampling; BOOL m_bCorrectScatteringAtDepthBreaks; BOOL m_bShowDepthBreaks; BOOL m_bShowLightingOnly; BOOL m_bOptimizeSampleLocations; BOOL m_bEnableLightShafts; uint m_uiInstrIntegralSteps; float2 m_f2ShadowMapTexelSize; uint m_uiShadowMapResolution; uint m_uiMinMaxShadowMapResolution; BOOL m_bUse1DMinMaxTree; float m_fMaxShadowMapStep; float m_fMiddleGray; uint m_uiLightSctrTechnique; int m_iNumCascades; int m_iFirstCascade; float m_fNumCascades; float m_fFirstCascade; uint m_uiCascadeProcessingMode; uint m_uiRefinementCriterion; BOOL m_bIs32BitMinMaxMipMap; uint m_uiMultipleScatteringMode; uint m_uiSingleScatteringMode; BOOL m_bAutoExposure; uint m_uiToneMappingMode; BOOL m_bLightAdaptation; float m_fWhitePoint; float m_fLuminanceSaturation; float2 f2Dummy; uint m_uiExtinctionEvalMode; BOOL m_bUseCustomSctrCoeffs; float m_fAerosolDensityScale; float m_fAerosolAbsorbtionScale; float4 m_f4CustomRlghBeta; float4 m_f4CustomMieBeta; #ifdef __cplusplus SPostProcessingAttribs() : m_uiNumEpipolarSlices(512), m_uiMaxSamplesInSlice(256), m_uiInitialSampleStepInSlice(16), // Note that sampling near the epipole is very cheap since only a few steps // required to perform ray marching m_uiEpipoleSamplingDensityFactor(2), m_fRefinementThreshold(0.03f), m_bShowSampling(FALSE), m_bCorrectScatteringAtDepthBreaks(FALSE), m_bShowDepthBreaks(FALSE), m_bShowLightingOnly(FALSE), m_bOptimizeSampleLocations(TRUE), m_bEnableLightShafts(TRUE), m_uiInstrIntegralSteps(30), m_bUse1DMinMaxTree(TRUE), m_fMaxShadowMapStep(16.f), m_f2ShadowMapTexelSize(0,0), m_uiMinMaxShadowMapResolution(0), m_fMiddleGray(0.18f), m_uiLightSctrTechnique(LIGHT_SCTR_TECHNIQUE_EPIPOLAR_SAMPLING), m_iNumCascades(0), m_iFirstCascade(1), m_fNumCascades(0), m_fFirstCascade(1), m_uiCascadeProcessingMode(CASCADE_PROCESSING_MODE_SINGLE_PASS), m_uiRefinementCriterion(REFINEMENT_CRITERION_INSCTR_DIFF), m_bIs32BitMinMaxMipMap(FALSE), m_uiMultipleScatteringMode(MULTIPLE_SCTR_MODE_UNOCCLUDED), m_uiSingleScatteringMode(SINGLE_SCTR_MODE_LUT), m_bAutoExposure(TRUE), m_uiToneMappingMode(TONE_MAPPING_MODE_UNCHARTED2), m_bLightAdaptation(TRUE), m_fWhitePoint(3.f), m_fLuminanceSaturation(1.f), m_uiExtinctionEvalMode(EXTINCTION_EVAL_MODE_EPIPOLAR), m_bUseCustomSctrCoeffs(FALSE), m_fAerosolDensityScale(1.f), m_fAerosolAbsorbtionScale(0.1f), m_f4CustomRlghBeta( 5.8e-6f, 13.5e-6f, 33.1e-6f, 0.f ), m_f4CustomMieBeta(2.0e-5f, 2.0e-5f, 2.0e-5f, 0.f) {} #endif }; CHECK_STRUCT_ALIGNMENT(SPostProcessingAttribs); struct SAirScatteringAttribs { // Angular Rayleigh scattering coefficient contains all the terms exepting 1 + cos^2(Theta): // Pi^2 * (n^2-1)^2 / (2*N) * (6+3*Pn)/(6-7*Pn) float4 f4AngularRayleighSctrCoeff; // Total Rayleigh scattering coefficient is the integral of angular scattering coefficient in all directions // and is the following: // 8 * Pi^3 * (n^2-1)^2 / (3*N) * (6+3*Pn)/(6-7*Pn) float4 f4TotalRayleighSctrCoeff; float4 f4RayleighExtinctionCoeff; // Note that angular scattering coefficient is essentially a phase function multiplied by the // total scattering coefficient float4 f4AngularMieSctrCoeff; float4 f4TotalMieSctrCoeff; float4 f4MieExtinctionCoeff; float4 f4TotalExtinctionCoeff; // Cornette-Shanks phase function (see Nishita et al. 93) normalized to unity has the following form: // F(theta) = 1/(4*PI) * 3*(1-g^2) / (2*(2+g^2)) * (1+cos^2(theta)) / (1 + g^2 - 2g*cos(theta))^(3/2) float4 f4CS_g; // x == 3*(1-g^2) / (2*(2+g^2)) // y == 1 + g^2 // z == -2*g float fEarthRadius; float fAtmTopHeight; float2 f2ParticleScaleHeight; float fTurbidity; float fAtmTopRadius; float m_fAerosolPhaseFuncG; float m_fDummy; #ifdef __cplusplus SAirScatteringAttribs(): f2ParticleScaleHeight(7994.f, 1200.f), // Air molecules and aerosols are assumed to be distributed // between 6360 km and 6420 km fEarthRadius(6360000.f), fAtmTopHeight(80000.f), fTurbidity(1.02f), m_fAerosolPhaseFuncG(0.76f) { fAtmTopRadius = fEarthRadius + fAtmTopHeight; } #endif }; CHECK_STRUCT_ALIGNMENT(SAirScatteringAttribs); struct SMiscDynamicParams { float fMaxStepsAlongRay; // Maximum number of steps during ray tracing float fCascadeInd; float2 f2WQ; // Used when pre-computing inscattering look-up table uint uiDepthSlice; float fElapsedTime; float2 f2Dummy; #ifdef __cplusplus uint ui4SrcMinMaxLevelXOffset; uint ui4SrcMinMaxLevelYOffset; uint ui4DstMinMaxLevelXOffset; uint ui4DstMinMaxLevelYOffset; #else uint4 ui4SrcDstMinMaxLevelOffset; #endif }; CHECK_STRUCT_ALIGNMENT(SMiscDynamicParams); #endif //_STRCUTURES_FXH_ ================================================ FILE: fx/Terrain.fx ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #include "TerrainStructs.fxh" #include "..\fx\Structures.fxh" // Texturing modes #define TM_HEIGHT_BASED 0 // Simple height-based texturing mode using 1D look-up table #define TM_MATERIAL_MASK 1 #define TM_MATERIAL_MASK_NM 2 #ifndef TEXTURING_MODE # define TEXTURING_MODE TM_MATERIAL_MASK_NM #endif #ifndef NUM_TILE_TEXTURES # define NUM_TILE_TEXTURES 5 #endif #ifndef NUM_SHADOW_CASCADES # define NUM_SHADOW_CASCADES 4 #endif static const float g_fEarthReflectance = 0.4f; cbuffer cbTerrainAttribs : register( b0 ) { STerrainAttribs g_TerrainAttribs; } cbuffer cbCameraAttribs : register( b1 ) { SCameraAttribs g_CameraAttribs; }; cbuffer cbLightAttribs : register( b2 ) { SLightAttribs g_LightAttribs; } cbuffer cbParticipatingMediaScatteringParams : register( b3 ) { SAirScatteringAttribs g_MediaParams; } cbuffer cbNMGenerationAttribs : register( b0 ) { SNMGenerationAttribs g_NMGenerationAttribs; } SamplerState samPointClamp : register( s0 ) { Filter = MIN_MAG_MIP_LINEAR; AddressU = Clamp; AddressV = Clamp; }; SamplerState samLinearMirror : register( s0 ) { Filter = MIN_MAG_MIP_LINEAR; AddressU = Mirror; AddressV = Mirror; }; SamplerState samLinearWrap : register( s1 ) { Filter = MIN_MAG_MIP_LINEAR; AddressU = Wrap; AddressV = Wrap; }; SamplerComparisonState samComparison : register (s2) { Filter = COMPARISON_MIN_MAG_LINEAR_MIP_POINT; AddressU = Clamp; AddressV = Clamp; }; SamplerState samLinearClamp : register( s3 ) { Filter = MIN_MAG_MIP_LINEAR; AddressU = Clamp; AddressV = Clamp; }; Texture2D g_tex2DElevationMap : register( t0 ); // Normal map stores only x,y components. z component is calculated as sqrt(1 - x^2 - y^2) Texture2D g_tex2DNormalMap : register( t1 ); Texture2D g_tex2DMtrlMap : register( t2 ); Texture2DArray g_tex2DShadowMap : register (t3); Texture2D g_tex2DTileTextures[NUM_TILE_TEXTURES] : register( t4 ); // Material texture Texture2D g_tex2DTileNormalMaps[NUM_TILE_TEXTURES] : register( t9 ); // Material texture //Texture2D g_tex2DElevationColor: register( t4 ); Texture2D g_tex2DOccludedNetDensityToAtmTop : register( t0 ); // Used in VS Texture2D g_tex2DAmbientSkylight : register( t1 ); // Used in VS #ifndef BEST_CASCADE_SEARCH # define BEST_CASCADE_SEARCH 1 #endif #ifndef SMOOTH_SHADOWS # define SMOOTH_SHADOWS 1 #endif void FindCascade(float3 f3PosInLightViewSpace, float fCameraViewSpaceZ, out float3 f3PosInCascadeProjSpace, out float3 f3CascadeLightSpaceScale, out float Cascade) { Cascade = 0; #if BEST_CASCADE_SEARCH while(Cascade < NUM_SHADOW_CASCADES) { // Find the smallest cascade which covers current point SCascadeAttribs CascadeAttribs = g_LightAttribs.ShadowAttribs.Cascades[Cascade]; f3CascadeLightSpaceScale = CascadeAttribs.f4LightSpaceScale.xyz; f3PosInCascadeProjSpace = f3PosInLightViewSpace * f3CascadeLightSpaceScale + g_LightAttribs.ShadowAttribs.Cascades[Cascade].f4LightSpaceScaledBias.xyz; // In order to perform PCF filtering without getting out of the cascade shadow map, // we need to be far enough from its boundaries. if( //Cascade == (NUM_SHADOW_CASCADES - 1) || all( abs(f3PosInCascadeProjSpace.xy) < 1/*- CascadeAttribs.f4LightProjSpaceFilterRadius.xy*/ ) && // It is necessary to check f3PosInCascadeProjSpace.z as well since it could be behind // the far clipping plane of the current cascade // Besides, if VSM or EVSM filtering is performed, there is also z boundary 0 /*+ CascadeAttribs.f4LightProjSpaceFilterRadius.z*/ < f3PosInCascadeProjSpace.z && f3PosInCascadeProjSpace.z < 1 /*- CascadeAttribs.f4LightProjSpaceFilterRadius.w*/ ) break; else Cascade++; } #else [unroll]for(int i=0; i<(NUM_SHADOW_CASCADES+3)/4; ++i) { float4 v = float4(g_LightAttribs.ShadowAttribs.f4CascadeCamSpaceZEnd[i] < fCameraViewSpaceZ); Cascade += dot(float4(1,1,1,1), v); } if( Cascade < NUM_SHADOW_CASCADES ) { //Cascade = min(Cascade, NUM_SHADOW_CASCADES - 1); f3CascadeLightSpaceScale = g_LightAttribs.ShadowAttribs.Cascades[Cascade].f4LightSpaceScale.xyz; f3PosInCascadeProjSpace = f3PosInLightViewSpace * f3CascadeLightSpaceScale + g_LightAttribs.ShadowAttribs.Cascades[Cascade].f4LightSpaceScaledBias.xyz; } #endif } float2 ComputeReceiverPlaneDepthBias(float3 ShadowUVDepthDX, float3 ShadowUVDepthDY) { // Compute (dDepth/dU, dDepth/dV): // // | dDepth/dU | | dX/dU dX/dV |T | dDepth/dX | | dU/dX dU/dY |-1T | dDepth/dX | // = = = // | dDepth/dV | | dY/dU dY/dV | | dDepth/dY | | dV/dX dV/dY | | dDepth/dY | // // | A B |-1 | D -B | | A B |-1T | D -C | // = / det = / det // | C D | |-C A | | C D | |-B A | // // | dDepth/dU | | dV/dY -dV/dX | | dDepth/dX | // = 1/det // | dDepth/dV | |-dU/dY dU/dX | | dDepth/dY | float2 biasUV; // dV/dY V dDepth/dX D dV/dX V dDepth/dY D biasUV.x = ShadowUVDepthDY.y * ShadowUVDepthDX.z - ShadowUVDepthDX.y * ShadowUVDepthDY.z; // dU/dY U dDepth/dX D dU/dX U dDepth/dY D biasUV.y = - ShadowUVDepthDY.x * ShadowUVDepthDX.z + ShadowUVDepthDX.x * ShadowUVDepthDY.z; float Det = (ShadowUVDepthDX.x * ShadowUVDepthDY.y) - (ShadowUVDepthDX.y * ShadowUVDepthDY.x); biasUV /= sign(Det) * max( abs(Det), 1e-20 ); //biasUV = abs(Det) > 1e-7 ? biasUV / abs(Det) : 0;// sign(Det) * max( abs(Det), 1e-10 ); return biasUV; } float ComputeShadowAmount(in float3 f3PosInLightViewSpace, in float fCameraSpaceZ, out float Cascade) { float3 f3PosInCascadeProjSpace = 0, f3CascadeLightSpaceScale = 0; FindCascade( f3PosInLightViewSpace.xyz, fCameraSpaceZ, f3PosInCascadeProjSpace, f3CascadeLightSpaceScale, Cascade); if( Cascade == NUM_SHADOW_CASCADES ) return 1; float3 f3ShadowMapUVDepth; f3ShadowMapUVDepth.xy = float2(0.5, 0.5) + float2(0.5, -0.5) * f3PosInCascadeProjSpace.xy; f3ShadowMapUVDepth.z = f3PosInCascadeProjSpace.z; float3 f3ddXShadowMapUVDepth = ddx(f3PosInLightViewSpace) * f3CascadeLightSpaceScale * float3(0.5,-0.5,1); float3 f3ddYShadowMapUVDepth = ddy(f3PosInLightViewSpace) * f3CascadeLightSpaceScale * float3(0.5,-0.5,1); float2 f2DepthSlopeScaledBias = ComputeReceiverPlaneDepthBias(f3ddXShadowMapUVDepth, f3ddYShadowMapUVDepth); float2 ShadowMapDim; float Elems; g_tex2DShadowMap.GetDimensions(ShadowMapDim.x, ShadowMapDim.y, Elems); f2DepthSlopeScaledBias /= ShadowMapDim.xy; float fractionalSamplingError = dot( float2(1.f, 1.f), abs(f2DepthSlopeScaledBias.xy) ); f3ShadowMapUVDepth.z += fractionalSamplingError; float fLightAmount = g_tex2DShadowMap.SampleCmp(samComparison, float3(f3ShadowMapUVDepth.xy, Cascade), float(f3ShadowMapUVDepth.z)).x; #if SMOOTH_SHADOWS int2 Offsets[] = { int2(-1,-1), int2(+1,-1), int2(-1,+1), int2(+1,+1), }; [unroll] for(int i=0; i<4; ++i) { float fDepthBias = dot(Offsets[i].xy, f2DepthSlopeScaledBias.xy); fLightAmount += g_tex2DShadowMap.SampleCmp(samComparison, float3(f3ShadowMapUVDepth.xy, Cascade), f3ShadowMapUVDepth.z + fDepthBias, Offsets[i]).x; } fLightAmount /= 5; #endif return fLightAmount; } void CombineMaterials(in float4 MtrlWeights, in float2 f2TileUV, out float3 SurfaceColor, out float3 SurfaceNormalTS) { SurfaceNormalTS = 0; // Normalize weights and compute base material weight MtrlWeights /= max( dot(MtrlWeights, float4(1,1,1,1)) , 1 ); float BaseMaterialWeight = saturate(1 - dot(MtrlWeights, float4(1,1,1,1))); // The mask is already sharp ////Sharpen the mask //float2 TmpMin2 = min(MtrlWeights.rg, MtrlWeights.ba); //float Min = min(TmpMin2.r, TmpMin2.g); //Min = min(Min, BaseMaterialWeight); //float p = 4; //BaseMaterialWeight = pow(BaseMaterialWeight-Min, p); //MtrlWeights = pow(MtrlWeights-Min, p); //float NormalizationFactor = dot(MtrlWeights, float4(1,1,1,1)) + BaseMaterialWeight; //MtrlWeights /= NormalizationFactor; //BaseMaterialWeight /= NormalizationFactor; // Get diffuse color of the base material float4 BaseMaterialDiffuse = g_tex2DTileTextures[0].Sample(samLinearWrap, f2TileUV.xy / g_TerrainAttribs.m_fBaseMtrlTilingScale); float4x4 MaterialColors = (float4x4)0; // Get tangent space normal of the base material #if TEXTURING_MODE == TM_MATERIAL_MASK_NM float3 BaseMaterialNormal = g_tex2DTileNormalMaps[0].Sample(samLinearWrap, f2TileUV.xy / g_TerrainAttribs.m_fBaseMtrlTilingScale); float4x3 MaterialNormals = (float4x3)0; #endif float4 f4TilingScale = g_TerrainAttribs.m_f4TilingScale; float fTilingScale[5] = {0, f4TilingScale.x, f4TilingScale.y, f4TilingScale.z, f4TilingScale.w}; // Load material colors and normals [unroll]for(int iTileTex = 1; iTileTex < NUM_TILE_TEXTURES; iTileTex++) { const float fThresholdWeight = 3.f/256.f; MaterialColors[iTileTex-1] = MtrlWeights[iTileTex-1] > fThresholdWeight ? g_tex2DTileTextures[iTileTex].Sample(samLinearWrap, f2TileUV.xy / fTilingScale[iTileTex]) : 0.f; #if TEXTURING_MODE == TM_MATERIAL_MASK_NM MaterialNormals[iTileTex-1] = MtrlWeights[iTileTex-1] > fThresholdWeight ? g_tex2DTileNormalMaps[iTileTex].Sample(samLinearWrap, f2TileUV.xy / fTilingScale[iTileTex]) : 0.f; #endif } // Blend materials and normals using the weights SurfaceColor = BaseMaterialDiffuse.rgb * BaseMaterialWeight + mul(MtrlWeights, MaterialColors).rgb; #if TEXTURING_MODE == TM_MATERIAL_MASK_NM SurfaceNormalTS = BaseMaterialNormal * BaseMaterialWeight + mul(MtrlWeights, MaterialNormals); SurfaceNormalTS = normalize(SurfaceNormalTS*2-1); #endif } RasterizerState RS_SolidFill;//Set by the app; can be biased or not //{ // FILLMODE = Solid; // CullMode = Back; // FrontCounterClockwise = true; //}; RasterizerState RS_SolidFill_NoCull { FILLMODE = Solid; CullMode = None; }; RasterizerState RS_Wireframe_NoCull { FILLMODE = Wireframe; CullMode = None; }; BlendState BS_DisableBlending { BlendEnable[0] = FALSE; BlendEnable[1] = FALSE; BlendEnable[2] = FALSE; }; DepthStencilState DSS_EnableDepthTest { DepthEnable = TRUE; DepthWriteMask = ALL; DEPTHFUNC = GREATER; }; DepthStencilState DSS_DisableDepthTest { DepthEnable = FALSE; DepthWriteMask = ZERO; }; struct SHemisphereVSOutput { float4 f4PosPS : SV_Position; float2 TileTexUV : TileTextureUV; float3 f3Normal : Normal; float3 f3PosInLightViewSpace : POS_IN_LIGHT_VIEW_SPACE; float fCameraSpaceZ : CAMERA_SPACE_Z; float2 f2MaskUV0 : MASK_UV0; float3 f3Tangent : TANGENT; float3 f3Bitangent : BITANGENT; float3 f3SunLightExtinction : EXTINCTION; float3 f3AmbientSkyLight : AMBIENT_SKY_LIGHT; }; void GetSunLightExtinctionAndSkyLight(in float3 f3PosWS, out float3 f3Extinction, out float3 f3AmbientSkyLight) { float3 f3EarthCentre = float3(0, -g_MediaParams.fEarthRadius, 0); float3 f3DirFromEarthCentre = f3PosWS - f3EarthCentre; float fDistToCentre = length(f3DirFromEarthCentre); f3DirFromEarthCentre /= fDistToCentre; float fHeightAboveSurface = fDistToCentre - g_MediaParams.fEarthRadius; float fCosZenithAngle = dot(f3DirFromEarthCentre, g_LightAttribs.f4DirOnLight.xyz); float fRelativeHeightAboveSurface = fHeightAboveSurface / g_MediaParams.fAtmTopHeight; float2 f2ParticleDensityToAtmTop = g_tex2DOccludedNetDensityToAtmTop.SampleLevel(samLinearClamp, float2(fRelativeHeightAboveSurface, fCosZenithAngle*0.5+0.5), 0).xy; float3 f3RlghOpticalDepth = g_MediaParams.f4RayleighExtinctionCoeff.rgb * f2ParticleDensityToAtmTop.x; float3 f3MieOpticalDepth = g_MediaParams.f4MieExtinctionCoeff.rgb * f2ParticleDensityToAtmTop.y; // And total extinction for the current integration point: f3Extinction = exp( -(f3RlghOpticalDepth + f3MieOpticalDepth) ); f3AmbientSkyLight = g_tex2DAmbientSkylight.SampleLevel(samLinearClamp, float2(fCosZenithAngle*0.5+0.5, 0.5), 0); } SHemisphereVSOutput HemisphereVS(in float3 f3PosWS : WORLD_POS, in float2 f2MaskUV0 : MASK0_UV) { SHemisphereVSOutput Out; Out.TileTexUV = f3PosWS.xz; Out.f4PosPS = mul( float4(f3PosWS,1), g_CameraAttribs.WorldViewProj); float4 ShadowMapSpacePos = mul( float4(f3PosWS,1), g_LightAttribs.ShadowAttribs.mWorldToLightView); Out.f3PosInLightViewSpace = ShadowMapSpacePos.xyz / ShadowMapSpacePos.w; Out.fCameraSpaceZ = Out.f4PosPS.w; Out.f2MaskUV0 = f2MaskUV0; float3 f3Normal = normalize(f3PosWS - float3(0, -g_TerrainAttribs.m_fEarthRadius, 0)); Out.f3Normal = f3Normal; Out.f3Tangent = normalize( cross(f3Normal, float3(0,0,1)) ); Out.f3Bitangent = normalize( cross(Out.f3Tangent, f3Normal) ); GetSunLightExtinctionAndSkyLight(f3PosWS, Out.f3SunLightExtinction, Out.f3AmbientSkyLight); return Out; } float4 HemisphereZOnlyVS(in float3 f3PosWS : WORLD_POS) : SV_Position { float4 f4PosPS = mul( float4(f3PosWS,1), g_CameraAttribs.WorldViewProj); return f4PosPS; } float3 HemispherePS(SHemisphereVSOutput In) : SV_Target { float3 EarthNormal = normalize(In.f3Normal); float3 EarthTangent = normalize(In.f3Tangent); float3 EarthBitangent = normalize(In.f3Bitangent); float3 f3TerrainNormal; f3TerrainNormal.xz = g_tex2DNormalMap.Sample(samLinearMirror, In.f2MaskUV0.xy).xy; // Since UVs are mirrored, we have to adjust normal coords accordingly: float2 f2XZSign = sign( 0.5 - frac(In.f2MaskUV0.xy/2) ); f3TerrainNormal.xz *= f2XZSign; f3TerrainNormal.y = sqrt( saturate(1 - dot(f3TerrainNormal.xz,f3TerrainNormal.xz)) ); //float3 Tangent = normalize(float3(1,0,In.HeightMapGradients.x)); //float3 Bitangent = normalize(float3(0,1,In.HeightMapGradients.y)); f3TerrainNormal = normalize( mul(f3TerrainNormal, float3x3(EarthTangent, EarthNormal, EarthBitangent)) ); float4 MtrlWeights = g_tex2DMtrlMap.Sample(samLinearMirror, In.f2MaskUV0.xy); float3 SurfaceColor, SurfaceNormalTS; CombineMaterials(MtrlWeights, In.TileTexUV, SurfaceColor.xyz, SurfaceNormalTS); float3 f3TerrainTangent = normalize( cross(f3TerrainNormal, float3(0,0,1)) ); float3 f3TerrainBitangent = normalize( cross(f3TerrainTangent, f3TerrainNormal) ); float3 f3Normal = normalize( mul(SurfaceNormalTS.xzy, float3x3(f3TerrainTangent, f3TerrainNormal, f3TerrainBitangent)) ); // Attenuate extraterrestrial sun color with the extinction factor float3 f3SunLight = g_LightAttribs.f4ExtraterrestrialSunColor.rgb * In.f3SunLightExtinction; // Ambient sky light is not pre-multiplied with the sun intensity float3 f3AmbientSkyLight = g_LightAttribs.f4ExtraterrestrialSunColor.rgb * In.f3AmbientSkyLight; // Account for occlusion by the ground plane f3AmbientSkyLight *= saturate((1 + dot(EarthNormal, f3Normal))/2.f); // We need to divide diffuse color by PI to get the reflectance value float3 SurfaceReflectance = SurfaceColor * g_fEarthReflectance / PI; float Cascade; float fLightAmount = ComputeShadowAmount(In.f3PosInLightViewSpace.xyz, In.fCameraSpaceZ, Cascade); float DiffuseIllumination = max(0, dot(f3Normal, g_LightAttribs.f4DirOnLight.xyz)); float3 f3CascadeColor = 0; if( g_LightAttribs.ShadowAttribs.bVisualizeCascades ) { f3CascadeColor = (Cascade < NUM_SHADOW_CASCADES ? g_TerrainAttribs.f4CascadeColors[Cascade].rgb : float3(1,1,1)) / 8 ; } float3 f3FinalColor = f3CascadeColor + SurfaceReflectance * (fLightAmount*DiffuseIllumination*f3SunLight + f3AmbientSkyLight); return f3FinalColor; } technique11 RenderHemisphereTech { pass P0 { SetVertexShader( CompileShader( vs_5_0, HemisphereVS() ) ); SetGeometryShader( NULL ); SetPixelShader( CompileShader( ps_5_0, HemispherePS() ) ); } } struct SScreenSizeQuadVSOutput { float4 m_f4Pos : SV_Position; float2 m_f2PosPS : PosPS; // Position in projection space [-1,1]x[-1,1] }; SScreenSizeQuadVSOutput GenerateScreenSizeQuadVS(in uint VertexId : SV_VertexID) { float4 MinMaxUV = float4(-1, -1, 1, 1); SScreenSizeQuadVSOutput Verts[4] = { {float4(MinMaxUV.xy, 1.0, 1.0), MinMaxUV.xy}, {float4(MinMaxUV.xw, 1.0, 1.0), MinMaxUV.xw}, {float4(MinMaxUV.zy, 1.0, 1.0), MinMaxUV.zy}, {float4(MinMaxUV.zw, 1.0, 1.0), MinMaxUV.zw} }; return Verts[VertexId]; } float3 ComputeNormal(float2 f2ElevMapUV, float fSampleSpacingInterval, float fMIPLevel) { # define GET_ELEV(Offset) g_tex2DElevationMap.SampleLevel( samPointClamp, f2ElevMapUV, fMIPLevel, Offset) #if 1 float Height00 = GET_ELEV( int2( -1, -1) ); float Height10 = GET_ELEV( int2( 0, -1) ); float Height20 = GET_ELEV( int2( +1, -1) ); float Height01 = GET_ELEV( int2( -1, 0) ); //float Height11 = GET_ELEV( int2( 0, 0) ); float Height21 = GET_ELEV( int2( +1, 0) ); float Height02 = GET_ELEV( int2( -1, +1) ); float Height12 = GET_ELEV( int2( 0, +1) ); float Height22 = GET_ELEV( int2( +1, +1) ); float3 Grad; Grad.x = (Height00+Height01+Height02) - (Height20+Height21+Height22); Grad.y = (Height00+Height10+Height20) - (Height02+Height12+Height22); Grad.z = fSampleSpacingInterval * 6.f; //Grad.x = (3*Height00+10*Height01+3*Height02) - (3*Height20+10*Height21+3*Height22); //Grad.y = (3*Height00+10*Height10+3*Height20) - (3*Height02+10*Height12+3*Height22); //Grad.z = fSampleSpacingInterval * 32.f; #else float Height1 = GET_ELEV( int2( 1, 0) ); float Height2 = GET_ELEV( int2(-1, 0) ); float Height3 = GET_ELEV( int2( 0, 1) ); float Height4 = GET_ELEV( int2( 0,-1) ); float3 Grad; Grad.x = Height2 - Height1; Grad.y = Height4 - Height3; Grad.z = fSampleSpacingInterval * 2.f; #endif Grad.xy *= HEIGHT_MAP_SCALE*g_NMGenerationAttribs.m_fElevationScale; float3 Normal = normalize( Grad ); return Normal; } float2 GenerateNormalMapPS(SScreenSizeQuadVSOutput In) : SV_TARGET { float2 f2UV = float2(0.5,0.5) + float2(0.5,-0.5) * In.m_f2PosPS.xy; float3 Normal = ComputeNormal( f2UV, g_NMGenerationAttribs.m_fSampleSpacingInterval*exp2(g_NMGenerationAttribs.m_fMIPLevel), g_NMGenerationAttribs.m_fMIPLevel ); // Only xy components are stored. z component is calculated in the shader return Normal.xy; } technique11 RenderNormalMapTech { pass { SetDepthStencilState( DSS_DisableDepthTest, 0 ); SetRasterizerState( RS_SolidFill_NoCull ); SetBlendState( BS_DisableBlending, float4( 0.0f, 0.0f, 0.0f, 0.0f ), 0xFFFFFFFF ); SetVertexShader( CompileShader( vs_5_0, GenerateScreenSizeQuadVS() ) ); SetGeometryShader( NULL ); SetPixelShader( CompileShader( ps_5_0, GenerateNormalMapPS() ) ); } } ================================================ FILE: fx/TerrainStructs.fxh ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #ifndef _TERRAIN_STRCUTS_FXH_ #define _TERRAIN_STRCUTS_FXH_ #include "Structures.fxh" #define PI 3.1415928f #define HEIGHT_MAP_SCALE 65535.f #ifndef __cplusplus # define D3DXVECTOR2 float2 # define D3DXVECTOR3 float3 # define D3DXVECTOR4 float4 #endif #ifdef __cplusplus # define CHECK_STRUCT_ALIGNMENT(s) static_assert( sizeof(s) % 16 == 0, "sizeof("#s") is not multiple of 16" ); #else # define CHECK_STRUCT_ALIGNMENT(s) #endif struct STerrainAttribs { float m_fElevationScale; float m_fElevationSamplingInterval; float m_fEarthRadius; float m_fBaseMtrlTilingScale; float4 m_f4TilingScale; float4 f4CascadeColors[MAX_CASCADES]; #ifdef __cplusplus STerrainAttribs() : m_fElevationScale(0.1f), m_fElevationSamplingInterval(1.f), m_fEarthRadius( SAirScatteringAttribs().fEarthRadius ), m_fBaseMtrlTilingScale(100.f), m_f4TilingScale(100.f, 100.f, 100.f, 100.f) { f4CascadeColors[0] = float4(0,1,0,1); f4CascadeColors[1] = float4(0,0,1,1); f4CascadeColors[2] = float4(1,1,0,1); f4CascadeColors[3] = float4(0,1,1,1); f4CascadeColors[4] = float4(1,0,1,1); f4CascadeColors[5] = float4(0.3f, 1, 0.7f,1); f4CascadeColors[6] = float4(0.7f, 0.3f,1,1); f4CascadeColors[7] = float4(1, 0.7f, 0.3f, 1); } #endif }; CHECK_STRUCT_ALIGNMENT(STerrainAttribs); struct SNMGenerationAttribs { float m_fSampleSpacingInterval; float m_fMIPLevel; float m_fElevationScale; float m_fDummy; }; CHECK_STRUCT_ALIGNMENT(SNMGenerationAttribs); #endif //_TERRAIN_STRCUTS_FXH_ ================================================ FILE: license.txt ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: You must give any other recipients of the Work or Derivative Works a copy of this License; and You must cause any modified files to carry prominent notices stating that You changed the files; and You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: media/Tiles/cliff_NM.dds ================================================ [File too large to display: 14.2 MB] ================================================ FILE: readme.txt ================================================ Intel Corporation - Outdoor Light Scattering Sample This sample shows how high-quality light scattering effects in large outdoor environments can be rendered on Intel HD graphics in real time. The technique extends the approach shown in the previously published sample and exploits the same main ideas. Epipolar sampling helps minimize the number of samples for which expensive ray marching algorithm is executed while 1D min/max binary trees are used to accelerate computations. In this sample, a more complex physical model is exploited to simulate light propagation in the atmosphere. It assumes that the planet is spherical and that the particle density decreases exponentially with the altitude computed with respect to the planet surface. To facilitate shadows in large outdoor environments, cascaded shadow maps are used. Scattering contribution from each cascade is computed and accumulated to obtain the final result. Note: Media Elements are the images, clip art, animations, sounds, music, shapes, video clips, 2D Images, 2D and 3D Meshs and mesh data, animation and animation data, and Textures included in the software. This license does not grant you any rights in the Media Elements and you may not reproduce, prepare derivative works, distribute, publicly display, or publicly perform the Media Elements. Note: The source code sample is provided under the BSD license. See the license folder within the sample source directory for additional details. ================================================ FILE: stdafx.h ================================================ ///////////////////////////////////////////////////////////////////////////////////////////// // Copyright 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. ///////////////////////////////////////////////////////////////////////////////////////////// #define WIN32_LEAN_AND_MEAN #pragma warning (disable: 4100 4127) // warning C4100: unreferenced formal parameter; warning C4127: conditional expression is constant #pragma warning (push) #pragma warning (disable: 4201) // nonstandard extension used : nameless struct/union // // windows headers // #include #include #include #include // for CComPtr support // // C++ headers // #include #include #include #include #include #include #include #include #include #include // // DirectX headers // #include #include #include #include #pragma warning (pop) #include "Errors.h" #include "CPUT.h" #if defined(DEBUG) || defined(_DEBUG) #ifndef V #define V(x) { hr = (x); assert(SUCCEEDED(hr)); } #endif #ifndef V_RETURN #define V_RETURN(x) { hr = (x); assert(SUCCEEDED(hr)); if( FAILED(hr) ) { return hr; } } #endif #else #ifndef V #define V(x) { hr = (x); } #endif #ifndef V_RETURN #define V_RETURN(x) { hr = (x); if( FAILED(hr) ) { return hr; } } #endif #endif //#define assert(x) if(!(x)) _CrtDbgBreak(); else {} inline void UnbindPSResources(ID3D11DeviceContext *pCtx) { ID3D11ShaderResourceView *pSRVs[20] = {NULL}; pCtx->PSSetShaderResources(0, _countof(pSRVs), pSRVs); } inline void UnbindVSResources(ID3D11DeviceContext *pCtx) { ID3D11ShaderResourceView *pSRVs[8] = {NULL}; pCtx->VSSetShaderResources(0, _countof(pSRVs), pSRVs); } inline void UpdateConstantBuffer(ID3D11DeviceContext *pDeviceCtx, ID3D11Buffer *pCB, const void *pData, size_t DataSize) { D3D11_MAPPED_SUBRESOURCE MappedData; pDeviceCtx->Map(pCB, 0, D3D11_MAP_WRITE_DISCARD, 0, &MappedData); memcpy(MappedData.pData, pData, DataSize); pDeviceCtx->Unmap(pCB, 0); } // end of file