diff --git a/Code/OysterGraphics/Core/CoreIncludes.h b/Code/OysterGraphics/Core/CoreIncludes.h index 3e1d9306..26b1ce0c 100644 --- a/Code/OysterGraphics/Core/CoreIncludes.h +++ b/Code/OysterGraphics/Core/CoreIncludes.h @@ -6,7 +6,7 @@ // http://lolengine.net/blog/2011/3/4/fuck-you-microsoft-near-far-macros #include #include -#include +#include #pragma comment(lib, "d3d11.lib") #pragma comment(lib, "d3dcompiler.lib") diff --git a/Code/OysterGraphics/FileLoader/ObjReader.cpp b/Code/OysterGraphics/FileLoader/ObjReader.cpp index e4d12b91..b5ad5d4e 100644 --- a/Code/OysterGraphics/FileLoader/ObjReader.cpp +++ b/Code/OysterGraphics/FileLoader/ObjReader.cpp @@ -1,268 +1,122 @@ -#include "ObjReader.h" -#include "Utilities.h" -#include "..\Core\Core.h" +#include "OBJReader.h" +#include #include -#include using namespace std; -using namespace Oyster::FileLoaders; -using namespace Oyster; -using namespace Oyster::Math; - -ObjReader *ObjReader::LoadFile(std::string fileName, Oyster::Math::Float4x4 transform) +OBJReader::OBJReader() { - static std::map cache; - - ObjReader *reader = NULL; - - if (cache.count(fileName)) - { - reader = cache[fileName]; - } - else - { - reader = new ObjReader(); - reader->ParseFile(fileName, transform); - - cache[fileName] = reader; - } - - return reader; + _mPos = 0; + _mNormal = 0; + _mTexel = 0; } -ObjReader::ObjReader(void) +OBJReader::~OBJReader() { + } - -ObjReader::~ObjReader(void) +void OBJReader::readOBJFile( wstring fileName ) { -} + fstream inStream; + string typeOfData = " "; + float vertexData; + string face1, face2, face3; -void ObjReader::ParseFile(std::string fileName, Float4x4 transform) -{ - ifstream input; - input.open(fileName.c_str()); - - if(!input.is_open()) + inStream.open( fileName, fstream::in ); + + if( inStream.is_open() ) { - return; - } - - string path; - Utility::String::extractDirPath(path,fileName,'\\'); - - std::vector VertexList; - std::vector vList; - std::vector nList; - std::vector uvList; - Vertex vertex1, vertex2, vertex3; - Float3 face[3]; - Float3 position, normal; - Float2 uv; - string s; - - while(!input.eof()) - { - getline(input,s); - int offset = (int)s.find(' '); - - if(offset!=-1) + while( !inStream.eof() ) { - string c = s.substr(0,offset); + inStream >> typeOfData; - if(c=="v") + if( typeOfData == "v" ) { - position = readVertex(offset,s); - vList.push_back(position); + Oyster::Math::Float3 position; + + inStream >> vertexData; + position.x = vertexData; + + inStream >> vertexData; + position.y = vertexData; + + inStream >> vertexData; + position.z = vertexData; + + _mVertexCoord.push_back( position ); + } - else if(c=="vt") + else if( typeOfData == "vt" ) { - uv = readUV(offset,s); - uvList.push_back(uv); + Oyster::Math::Float2 texel; + inStream >> vertexData; + texel.x = vertexData; + + inStream >> vertexData; + texel.y = 1 - vertexData; + + _mVertexTexture.push_back( texel ); } - else if(c=="vn") + else if( typeOfData == "vn" ) { - normal = readNormal(offset,s); - nList.push_back(normal); + Oyster::Math::Float3 normal; + inStream >> vertexData; + normal.x = vertexData; + + inStream >> vertexData; + normal.y = vertexData; + + inStream >> vertexData; + normal.z = vertexData; + + _mVertexNormal.push_back( normal ); } - else if(c=="f") + else if( typeOfData == "f" ) { - readFace(offset, s, face); + inStream >> face1; + stringSplit( face1 ); + + addToOBJarray(); - vertex1.Position = vList[(int)face[0].x]; - vertex1.UV = uvList[(int)face[0].y]; - vertex1.Normal = nList[(int)face[0].z]; + inStream >> face2; + stringSplit( face2 ); - vertex2.Position = vList[(int)face[1].x]; - vertex2.UV = uvList[(int)face[1].y]; - vertex2.Normal = nList[(int)face[1].z]; + addToOBJarray(); - vertex3.Position = vList[(int)face[2].x]; - vertex3.UV = uvList[(int)face[2].y]; - vertex3.Normal = nList[(int)face[2].z]; + inStream >> face3; + stringSplit( face3 ); - VertexList.push_back(vertex1); - VertexList.push_back(vertex3); - VertexList.push_back(vertex2); - } - else if(c=="mtllib") - { - this->materials = GetMaterials(path+s.substr(offset+1)); + addToOBJarray(); } } } - input.close(); - - this->numVertices = VertexList.size(); - this->vertices = new Vertex[this->numVertices]; - - for(size_t i=0;inumVertices;++i) - { - vertices[i].Position=Math::transformVector(Math::Float4(VertexList[i].Position,1),transform); - vertices[i].Normal=Math::transformVector(Math::Float4(VertexList[i].Normal,0),transform); - vertices[i].UV = VertexList[i].UV; - } + inStream.close(); } -void ObjReader::GetVertexData(Vertex **vertex,int &numVertex, std::map &Textures) +//Private functions +void OBJReader::stringSplit( string strToSplit ) { - numVertex=(int)this->numVertices; - (*vertex)=this->vertices; - Textures = this->materials; + char delim = '/'; + string vPos, vNormal, vTexel; + stringstream aStream(strToSplit); + getline( aStream, vPos, delim ); + getline( aStream, vTexel, delim ); + getline( aStream, vNormal ); + + _mPos = atoi( vPos.c_str() ); + _mNormal = atoi( vNormal.c_str() ); + _mTexel = atoi( vTexel.c_str() ); + } -Float3 ObjReader::extract(std::string d) +void OBJReader::addToOBJarray() { - Float3 data; - int offset=(int)d.find('/'); - data.x=(float)atoi(d.substr(1,offset).c_str())-1; + OBJFormat temp; - int newOffset = (int)d.find('/',offset+1); - string d2=d.substr(offset+1,newOffset-offset-1); - data.y=(float)atoi(d2.c_str())-1; - offset=newOffset; + temp._d3VertexCoord = _mVertexCoord.at( _mPos - 1 ); + temp._d3VertexNormal = _mVertexNormal.at( _mNormal - 1 ); + temp._d3VertexTexture = _mVertexTexture.at( _mTexel - 1 ); - newOffset = (int)d.find('/',offset+1); - string d3=d.substr(offset+1,newOffset-offset-1); - data.z=(float)atoi(d3.c_str())-1; - - return data; -} - -Float3 ObjReader::readVertex(int offset,string s) -{ - int newOffset = (int)s.find(' ',offset+1); - Float3 vertex; - string d = s.substr(offset,newOffset-offset); - vertex.x = (float)atof(d.c_str()); - offset=newOffset; - - newOffset = (int)s.find(' ',offset+1); - vertex.y = (float)atof(s.substr(offset,newOffset-offset).c_str()); - offset=newOffset; - - newOffset = (int)s.find(' ',offset+1); - vertex.z = (float)-atof(s.substr(offset,newOffset-offset).c_str()); - - return vertex; -} - -Float2 ObjReader::readUV(int offset,string s) -{ - int newOffset = (int)s.find(' ',offset+1); - Float2 uv; - string d = s.substr(offset,newOffset-offset); - uv.x =(float)atof(d.c_str()); - offset=newOffset; - - newOffset = (int)s.find(' ',offset+1); - d = s.substr(offset,newOffset-offset); - uv.y =1- (float)atof(d.c_str()); - offset=newOffset; - - return uv; -} - -Float3 ObjReader::readNormal(int offset,string s) -{ - int newOffset = (int)s.find(' ',offset+1); - Float3 vertex; - string d = s.substr(offset,newOffset-offset); - vertex.x = (float)atof(d.c_str()); - offset=newOffset; - - newOffset = (int)s.find(' ',offset+1); - vertex.y = (float)atof(s.substr(offset,newOffset-offset).c_str()); - offset=newOffset; - - newOffset = (int)s.find(' ',offset+1); - vertex.z = (float)-atof(s.substr(offset,newOffset-offset).c_str()); - - return vertex; -} - -void ObjReader::readFace(int offset,string s, Oyster::Math::Float3 face[3]) -{ - int newOffset = (int)s.find(' ',offset+1); - string point1 = s.substr(offset,newOffset-offset); - - offset = newOffset; - newOffset = (int)s.find(' ',offset+1); - string point2 = s.substr(offset,newOffset-offset); - - offset = newOffset; - newOffset = (int)s.find(' ',offset+1); - string point3 = s.substr(offset,newOffset-offset); - - face[0] = extract(point1); - face[1] = extract(point2); - face[2] = extract(point3); -} - -std::map ObjReader::GetMaterials(std::string fileName) -{ - ifstream input; - input.open(fileName.c_str()); - - std::map materials; - ID3D11ShaderResourceView *srv; - string texture; - string s; - string path; - Utility::String::extractDirPath(path,fileName,'\\'); - if(!input.is_open()) - return materials; - - while(!input.eof()) - { - getline(input,s); - int offset = (int)s.find(' '); - if(offset!=-1) - { - string c = s.substr(0,offset); - if(c=="map_Kd") - { - texture = path+s.substr(offset+1); - D3DX11CreateShaderResourceViewFromFile(Oyster::Core::Device,texture.c_str(), NULL, NULL, &srv, NULL); - materials["Diffuse"] = srv; - } - if(c=="map_G") - { - texture = path+s.substr(offset+1); - D3DX11CreateShaderResourceViewFromFile(Oyster::Core::Device,texture.c_str(), NULL, NULL, &srv, NULL); - materials["Glow"] = srv; - } - if(c=="map_Ks") - { - texture = path+s.substr(offset+1); - D3DX11CreateShaderResourceViewFromFile(Oyster::Core::Device,texture.c_str(), NULL, NULL, &srv, NULL); - materials["Specular"] = srv; - } - } - } - input.close(); - - return materials; -} + _myOBJ.push_back( temp ); +} \ No newline at end of file diff --git a/Code/OysterGraphics/FileLoader/ObjReader.h b/Code/OysterGraphics/FileLoader/ObjReader.h index a846181e..562d81fc 100644 --- a/Code/OysterGraphics/FileLoader/ObjReader.h +++ b/Code/OysterGraphics/FileLoader/ObjReader.h @@ -1,42 +1,53 @@ -#pragma once -#include "..\Core\CoreIncludes.h" -#include "..\Math\OysterMath.h" +#ifndef OBJREADER_H +#define OBJREADER_H +#include "..\..\Misc\Utilities.h" +#include "..\..\OysterMath\OysterMath.h" -namespace Oyster +//#include + + + +class OBJReader { - namespace FileLoaders - { - class ObjReader + public: + struct OBJFormat { - public: - struct Vertex - { - Oyster::Math::Float3 Position; - Oyster::Math::Float3 Normal; - Oyster::Math::Float2 UV; - }; - - static ObjReader *LoadFile(std::string fileName, Oyster::Math::Float4x4 transform = Oyster::Math::Float4x4::identity); - - ObjReader(void); - ~ObjReader(void); - - void GetVertexData(Vertex **vertex,int &numVertex, std::map &textures); - - private: - Vertex *vertices; - size_t numVertices; - std::map materials; - - void ParseFile(std::string fileName, Oyster::Math::Float4x4 transform = Oyster::Math::Float4x4::identity); - - Oyster::Math::Float3 extract(std::string d); - Oyster::Math::Float3 readVertex(int offset,std::string s); - Oyster::Math::Float2 readUV(int offset,std::string s); - Oyster::Math::Float3 readNormal(int offset,std::string s); - void readFace(int offset,std::string s, Oyster::Math::Float3 face[3]); - - std::map GetMaterials(std::string fileName); + Oyster::Math::Float3 _d3VertexCoord; + Oyster::Math::Float2 _d3VertexTexture; + Oyster::Math::Float3 _d3VertexNormal; }; - } -} \ No newline at end of file + + struct OBJMaterialData + { + string _name; + string _mapKd; + float _kd[3]; + float _ka[3]; + float _tf[3]; + float _ni; + + OBJMaterialData() + { + _name = " "; + _mapKd = " "; + } + }; + std::vector _myOBJ; + private: + + vector _mVertexCoord, _mVertexNormal; + vector _mVertexTexture; + + int _mNrOfCoords, _mNrOfNormals, _mNrOfTexels, _mNrOfFaces; + int _mPos, _mNormal, _mTexel; + void stringSplit( string strToSplit ); + void addToOBJarray(); + + public: + OBJReader(); + ~OBJReader(); + + void readOBJFile( wstring fileName); + +}; +#endif \ No newline at end of file diff --git a/OysterGraphics/Core/ShaderManager.cpp b/OysterGraphics/Core/ShaderManager.cpp new file mode 100644 index 00000000..ca607249 --- /dev/null +++ b/OysterGraphics/Core/ShaderManager.cpp @@ -0,0 +1,351 @@ +#include "Core.h" +#include + +const char* ShaderFunction = "main"; + +namespace Oyster +{ + bool LoadPrecompiled(std::wstring filename, Core::ShaderManager::ShaderType type, std::wstring name); + bool LoadCompile(std::wstring filename, Core::ShaderManager::ShaderType type, std::wstring name); + namespace + { + struct ShaderData + { + size_t size; + char* data; + }; + std::vector PS; + std::map PSMap; + + std::vector GS; + std::map GSMap; + + std::vector CS; + std::map CSMap; + + std::vector VS; + std::vector VData; + std::map VSMap; + } + +#pragma region Init + bool Core::ShaderManager::Init(std::wstring filename, ShaderType type, std::wstring name, bool Precompiled) + { + if(Precompiled) + { + return LoadPrecompiled(filename, type, name); + } + else + { + return LoadCompile(filename, type, name); + } + return true; + } + + bool LoadPrecompiled(std::wstring filename, Core::ShaderManager::ShaderType type, std::wstring name) + { + + std::ifstream stream; + ShaderData sd; + + + //Create Vertex shader and Layout + stream.open(filename, std::ifstream::in | std::ifstream::binary); + if(stream.good()) + { + stream.seekg(0, std::ios::end); + sd.size = size_t(stream.tellg()); + sd.data = new char[sd.size]; + stream.seekg(0, std::ios::beg); + stream.read(&sd.data[0], sd.size); + stream.close(); + + + ID3D11VertexShader* vertex; + ID3D11GeometryShader* geometry; + ID3D11PixelShader* pixel; + ID3D11ComputeShader* compute; + + switch(type) + { + case Core::ShaderManager::ShaderType::Vertex: + + if(VSMap.count(name)) + return false; + + if(FAILED(Core::Device->CreateVertexShader(sd.data, sd.size, 0, &vertex))) + { + return false; + } + VSMap[name] = VS.size(); + VS.push_back(vertex); + VData.push_back(sd); + break; + + case Core::ShaderManager::ShaderType::Hull: + case Core::ShaderManager::ShaderType::Domain: + + return false; + + case Core::ShaderManager::ShaderType::Geometry: + + if(GSMap.count(name)) + return false; + if(FAILED(Core::Device->CreateGeometryShader(sd.data, sd.size, 0, &geometry))) + { + return false; + } + GSMap[name] = GS.size(); + GS.push_back(geometry); + break; + + case Core::ShaderManager::ShaderType::Pixel: + + if(PSMap.count(name)) + return false; + if(FAILED(Core::Device->CreatePixelShader(sd.data, sd.size, 0, &pixel))) + { + return false; + } + PSMap[name] = PS.size(); + PS.push_back(pixel); + break; + + case Core::ShaderManager::ShaderType::Compute: + + if(CSMap.count(name)) + return false; + if(FAILED(Core::Device->CreateComputeShader(sd.data, sd.size, 0, &compute))) + { + return false; + } + CSMap[name] = CS.size(); + CS.push_back(compute); + break; + } + } + else + { + return false; + } + return true; + } + + bool LoadCompile(std::wstring filename, Core::ShaderManager::ShaderType type, std::wstring name) + { + /// \todo error reporting + ID3D10Blob *Shader,*Error; + switch(type) + { + case Core::ShaderManager::ShaderType::Pixel: + + ID3D11PixelShader* pixel; + + if(FAILED(D3DCompileFromFile(filename.c_str(),NULL,NULL,ShaderFunction,"ps_5_0",0,0,&Shader,&Error))) + { + std::string fel = (char*)Error->GetBufferPointer(); + Error->Release(); + return false; + } + if(FAILED(Oyster::Core::Device->CreatePixelShader(Shader->GetBufferPointer(),Shader->GetBufferSize(),NULL,&pixel))) + { + Error->Release(); + Shader->Release(); + return false; + } + Shader->Release(); + if(!PSMap.count(name)) + { + PSMap[name] = PS.size(); + PS.push_back(pixel); + } + else + { + PS[PSMap[name]] = pixel; + } + break; + + case Core::ShaderManager::ShaderType::Geometry: + + ID3D11GeometryShader* geometry; + + if(FAILED(D3DCompileFromFile(filename.c_str(),NULL,NULL,ShaderFunction,"gs_5_0",0,0,&Shader,&Error))) + { + std::string fel = (char*)Error->GetBufferPointer(); + Error->Release(); + return false; + } + if(FAILED(Oyster::Core::Device->CreateGeometryShader(Shader->GetBufferPointer(),Shader->GetBufferSize(),NULL,&geometry))) + { + Error->Release(); + Shader->Release(); + return false; + } + Shader->Release(); + if(!GSMap.count(name)) + { + GSMap[name] = GS.size(); + GS.push_back(geometry); + } + else + { + GS[GSMap[name]] = geometry; + } + break; + + case Core::ShaderManager::ShaderType::Vertex: + + ID3D11VertexShader* vertex; + + if(FAILED(D3DCompileFromFile(filename.c_str(),NULL,NULL,ShaderFunction,"vs_5_0",0,0,&Shader,&Error))) + { + std::string fel = (char*)Error->GetBufferPointer(); + Error->Release(); + return false; + } + if(FAILED(Oyster::Core::Device->CreateVertexShader(Shader->GetBufferPointer(),Shader->GetBufferSize(),NULL,&vertex))) + { + Error->Release(); + Shader->Release(); + return false; + } + if(!VSMap.count(name)) + { + VSMap[name] = VS.size(); + VS.push_back(vertex); + ShaderData sd; + sd.size = Shader->GetBufferSize(); + sd.data = new char[sd.size]; + memcpy(sd.data,Shader->GetBufferPointer(),sd.size); + VData.push_back(sd); + } + else + { + VS[VSMap[name]] = vertex; + delete[] VData[VSMap[name]].data; + VData[VSMap[name]].size = Shader->GetBufferSize(); + VData[VSMap[name]].data = new char[VData[VSMap[name]].size]; + memcpy(VData[VSMap[name]].data,Shader->GetBufferPointer(),VData[VSMap[name]].size); + } + + Shader->Release(); + break; + + } + return true; + } +#pragma endregion + + void Core::ShaderManager::CreateInputLayout(const D3D11_INPUT_ELEMENT_DESC *desc, int ElementCount,int VertexIndex,ID3D11InputLayout *&Layout) + { + if(VertexIndex==-1) + { + Layout=0; + return; + } + Device->CreateInputLayout(desc,ElementCount,VData[VertexIndex].data,VData[VertexIndex].size,&Layout); + } + +#pragma region Get + int Core::ShaderManager::Get::Pixel(std::wstring Name) + { + if(PSMap.count(Name)) + return PSMap[Name]; + return -1; + } + + int Core::ShaderManager::Get::Vertex(std::wstring Name) + { + if(VSMap.count(Name)) + return VSMap[Name]; + return -1; + } + int Core::ShaderManager::Get::Geometry(std::wstring Name) + { + if(GSMap.count(Name)) + return GSMap[Name]; + return -1; + } + + int Core::ShaderManager::Get::Compute(std::wstring Name) + { + if(CSMap.count(Name)) + return CSMap[Name]; + return -1; + } + int Core::ShaderManager::Get::Hull(std::wstring Name) + { + return -1; + } + int Core::ShaderManager::Get::Domain(std::wstring Name) + { + return -1; + } +#pragma endregion + +#pragma region Set + /// \todo smart set + void Core::ShaderManager::Set::Pixel(int Index) + { + if(Index==-1) + DeviceContext->PSSetShader( NULL,NULL,0); + else + DeviceContext->PSSetShader( PS[Index],NULL,0); + } + void Core::ShaderManager::Set::Vertex(int Index) + { + if(Index==-1) + DeviceContext->VSSetShader( NULL,NULL,0); + else + DeviceContext->VSSetShader( VS[Index],NULL,0); + } + void Core::ShaderManager::Set::Geometry(int Index) + { + if(Index==-1) + DeviceContext->GSSetShader( NULL,NULL,0); + else + DeviceContext->GSSetShader( GS[Index],NULL,0); + } + void Core::ShaderManager::Set::Compute(int Index) + { + if(Index==-1) + DeviceContext->CSSetShader( NULL,NULL,0); + else + DeviceContext->CSSetShader( CS[Index],NULL,0); + } + /// \todo set Hull + void Core::ShaderManager::Set::Hull(int Index) + { + return; + } + /// \todo set Domain + void Core::ShaderManager::Set::Domain(int Index) + { + return; + } +#pragma endregion + + /// \todo smart Set ie. not resetting the shader + /// \todo research states + /// \todo smart buffer set + void Core::ShaderManager::SetShaderEffect(ShaderEffect se) + { + Set::Pixel(se.Shaders.Pixel); + Set::Vertex(se.Shaders.Vertex); + Set::Geometry(se.Shaders.Geometry); + Set::Compute(se.Shaders.Compute); + Oyster::Core::DeviceContext->IASetInputLayout(se.IAStage.Layout); + Oyster::Core::DeviceContext->IASetPrimitiveTopology(se.IAStage.Topology); + for(unsigned int i=0;iApply(i); + for(unsigned int i=0;iApply(i); + for(unsigned int i=0;iApply(i); + Oyster::Core::DeviceContext->RSSetState(se.RenderStates.Rasterizer); + Oyster::Core::DeviceContext->PSSetSamplers(0,se.RenderStates.SampleCount,se.RenderStates.SampleState); + float test[4] = {0}; + Oyster::Core::DeviceContext->OMSetBlendState(se.RenderStates.BlendState,test,-1); + } + +} \ No newline at end of file diff --git a/OysterGraphics/OysterGraphics.vcxproj.filters b/OysterGraphics/OysterGraphics.vcxproj.filters new file mode 100644 index 00000000..412e083a --- /dev/null +++ b/OysterGraphics/OysterGraphics.vcxproj.filters @@ -0,0 +1,100 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {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 + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + + + \ No newline at end of file diff --git a/Tester/Tester.vcxproj b/Tester/Tester.vcxproj new file mode 100644 index 00000000..ccfb910f --- /dev/null +++ b/Tester/Tester.vcxproj @@ -0,0 +1,98 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {1B3BEA4C-CF75-438A-9693-60FB8444BBF3} + Win32Proj + Tester + + + + Application + true + v110 + Unicode + + + Application + false + v110 + true + Unicode + + + + + + + + + + + + + true + + + false + + + + + + Level3 + Disabled + WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + ..\OysterGraphics;..\OysterMath;..\Misc;%(AdditionalIncludeDirectories) + + + Windows + true + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + + + Windows + true + true + true + + + + + + + + {2ec4dded-8f75-4c86-a10b-e1e8eb29f3ee} + + + {0ec83e64-230e-48ef-b08c-6ac9651b4f82} + + + {f10cbc03-9809-4cba-95d8-327c287b18ee} + + + + + + \ No newline at end of file