diff --git a/enginecustom/Lightshaderclass.cpp b/enginecustom/Lightshaderclass.cpp index 9c483e2..e137cd9 100644 --- a/enginecustom/Lightshaderclass.cpp +++ b/enginecustom/Lightshaderclass.cpp @@ -475,10 +475,10 @@ bool LightShaderClass::SetShaderParameters(ID3D11DeviceContext* deviceContext, X dataPtr2 = (LightPositionBufferType*)mappedResource.pData; // Copy the light position variables into the constant buffer. - dataPtr2->lightPosition[0] = lightPosition[0]; - dataPtr2->lightPosition[1] = lightPosition[1]; - dataPtr2->lightPosition[2] = lightPosition[2]; - dataPtr2->lightPosition[3] = lightPosition[3]; + for (int i = 0; i < NUM_LIGHTS; i++) + { + dataPtr2->lightPosition[i] = lightPosition[i]; + } // Unlock the constant buffer. deviceContext->Unmap(m_lightPositionBuffer, 0); @@ -504,10 +504,10 @@ bool LightShaderClass::SetShaderParameters(ID3D11DeviceContext* deviceContext, X dataPtr3 = (LightColorBufferType*)mappedResource.pData; // Copy the light color variables into the constant buffer. - dataPtr3->diffuseColor[0] = diffuseColor[0]; - dataPtr3->diffuseColor[1] = diffuseColor[1]; - dataPtr3->diffuseColor[2] = diffuseColor[2]; - dataPtr3->diffuseColor[3] = diffuseColor[3]; + for (int i = 0; i < NUM_LIGHTS; i++) + { + dataPtr3->diffuseColor[i] = diffuseColor[i]; + } // Unlock the constant buffer. deviceContext->Unmap(m_lightColorBuffer, 0); diff --git a/enginecustom/Lightshaderclass.h b/enginecustom/Lightshaderclass.h index eb32019..3cf4974 100644 --- a/enginecustom/Lightshaderclass.h +++ b/enginecustom/Lightshaderclass.h @@ -9,7 +9,6 @@ // GLOBALS // ///////////// const int NUM_LIGHTS = 4; - ////////////// // INCLUDES // ////////////// @@ -88,7 +87,6 @@ private: ID3D11Buffer* m_lightBuffer; ID3D11Buffer* m_lightColorBuffer; ID3D11Buffer* m_lightPositionBuffer; - }; #endif \ No newline at end of file diff --git a/enginecustom/Logger.h b/enginecustom/Logger.h index bd1e35f..6fb69b4 100644 --- a/enginecustom/Logger.h +++ b/enginecustom/Logger.h @@ -24,7 +24,20 @@ public: { Info, Warning, - Error + Error, + Shutdown, + Initialize, + Update, + Render, + Input, + Physics, + Audio, + Network, + Scripting, + AI, + Resource, + Memory, + Debug }; Logger() diff --git a/enginecustom/Systemclass.cpp b/enginecustom/Systemclass.cpp index d1a0b6c..95a6451 100644 --- a/enginecustom/Systemclass.cpp +++ b/enginecustom/Systemclass.cpp @@ -25,7 +25,7 @@ bool SystemClass::Initialize() int screenWidth, screenHeight; bool result; - Logger::Get().Log("Initializing system class", __FILE__, __LINE__); + Logger::Get().Log("Initializing system class", __FILE__, __LINE__, Logger::LogLevel::Initialize); try { @@ -81,50 +81,50 @@ bool SystemClass::Initialize() return false; } - Logger::Get().Log("System class initialized", __FILE__, __LINE__); + Logger::Get().Log("System class initialized", __FILE__, __LINE__, Logger::LogLevel::Initialize); return true; } void SystemClass::Shutdown() { - Logger::Get().Log("Shutting down system class", __FILE__, __LINE__); + Logger::Get().Log("Shutting down system class", __FILE__, __LINE__, Logger::LogLevel::Shutdown); std::lock_guard guard(renderMutex); // Shutdown imgui if (m_imguiManager) { - Logger::Get().Log("Shutting down imgui manager", __FILE__, __LINE__); + Logger::Get().Log("Shutting down imgui manager", __FILE__, __LINE__, Logger::LogLevel::Shutdown); m_imguiManager->Shutdown(); delete m_imguiManager; m_imguiManager = 0; - Logger::Get().Log("Imgui manager shut down", __FILE__, __LINE__); + Logger::Get().Log("Imgui manager shut down", __FILE__, __LINE__, Logger::LogLevel::Shutdown); } // Release the application class object. if (m_Application) { - Logger::Get().Log("Shutting down application", __FILE__, __LINE__); + Logger::Get().Log("Shutting down application", __FILE__, __LINE__, Logger::LogLevel::Shutdown); m_Application->Shutdown(); delete m_Application; m_Application = 0; - Logger::Get().Log("Application shut down", __FILE__, __LINE__); + Logger::Get().Log("Application shut down", __FILE__, __LINE__, Logger::LogLevel::Shutdown); } // Release the input object. if (m_Input) { - Logger::Get().Log("Shutting down input", __FILE__, __LINE__); + Logger::Get().Log("Shutting down input", __FILE__, __LINE__, Logger::LogLevel::Shutdown); delete m_Input; m_Input = 0; - Logger::Get().Log("Input shut down", __FILE__, __LINE__); + Logger::Get().Log("Input shut down", __FILE__, __LINE__, Logger::LogLevel::Shutdown); } @@ -132,7 +132,7 @@ void SystemClass::Shutdown() // Shutdown the window. ShutdownWindows(); - Logger::Get().Log("System class shut down", __FILE__, __LINE__); + Logger::Get().Log("System class shut down", __FILE__, __LINE__, Logger::LogLevel::Shutdown); return; } @@ -328,7 +328,7 @@ void SystemClass::InitializeWindows(int& screenWidth, int& screenHeight) DEVMODE dmScreenSettings; int posX, posY; - Logger::Get().Log("Initializing windows", __FILE__, __LINE__); + Logger::Get().Log("Initializing windows", __FILE__, __LINE__, Logger::LogLevel::Initialize); // Get an external pointer to this object. ApplicationHandle = this; @@ -408,10 +408,7 @@ void SystemClass::InitializeWindows(int& screenWidth, int& screenHeight) void SystemClass::ShutdownWindows() { - - Logger::Get().Log("Shutting down windows", __FILE__, __LINE__); - - Logger::Get().Log("Shutting down the windows", __FILE__, __LINE__); + Logger::Get().Log("Shutting down the windows", __FILE__, __LINE__, Logger::LogLevel::Shutdown); // Show the mouse cursor. ShowCursor(true); diff --git a/enginecustom/applicationclass.cpp b/enginecustom/applicationclass.cpp index 650977c..d9e97ef 100644 --- a/enginecustom/applicationclass.cpp +++ b/enginecustom/applicationclass.cpp @@ -197,7 +197,6 @@ bool ApplicationClass::Initialize(int screenWidth, int screenHeight, HWND hwnd) // Set the number of lights we will use. m_numLights = 4; - // Create and initialize the light objects array. m_Lights.resize(m_numLights); @@ -208,28 +207,26 @@ bool ApplicationClass::Initialize(int screenWidth, int screenHeight, HWND hwnd) m_Lights[0]->SetSpecularPower(16.0f); m_Lights[0]->SetPosition(10.0f, 7.0f, -5.0f); - // Manually set the color and position of each light. m_Lights[1] = new LightClass; m_Lights[1]->SetDiffuseColor(1.0f, 0.0f, 0.0f, 1.0f); // Red - m_Lights[1]->SetDirection(0.0f, 0.0f, 1.0f); + m_Lights[1]->SetDirection(0.0f, 0.0f, -1.0f); m_Lights[1]->SetSpecularColor(1.0f, 0.0f, 0.0f, 1.0f); m_Lights[1]->SetSpecularPower(16.0f); - m_Lights[1]->SetPosition(10.0f, 7.0f, -5.0f); + m_Lights[1]->SetPosition(-10.0f, 7.0f, -5.0f); m_Lights[2] = new LightClass; m_Lights[2]->SetDiffuseColor(0.0f, 1.0f, 0.0f, 1.0f); // Green - m_Lights[2]->SetDirection(0.0f, 0.0f, 1.0f); + m_Lights[2]->SetDirection(0.0f, 0.0f, -1.0f); m_Lights[2]->SetSpecularColor(0.0f, 1.0f, 0.0f, 1.0f); m_Lights[2]->SetSpecularPower(16.0f); - m_Lights[2]->SetPosition(10.0f, 7.0f, -5.0f); + m_Lights[2]->SetPosition(10.0f, 7.0f, 5.0f); m_Lights[3] = new LightClass; m_Lights[3]->SetDiffuseColor(0.0f, 0.0f, 1.0f, 1.0f); // Blue - m_Lights[3]->SetDirection(0.0f, 0.0f, 1.0f); + m_Lights[3]->SetDirection(0.0f, 0.0f, -1.0f); m_Lights[3]->SetSpecularColor(0.0f, 0.0f, 1.0f, 1.0f); m_Lights[3]->SetSpecularPower(16.0f); - m_Lights[3]->SetPosition(10.0f, 7.0f, -5.0f); - + m_Lights[3]->SetPosition(-10.0f, 7.0f, 5.0f); // Create and initialize the normal map shader object. m_ShaderManager = new ShaderManagerClass; @@ -666,7 +663,7 @@ bool ApplicationClass::RenderSceneToTexture(float rotation) m_Model->Render(m_Direct3D->GetDeviceContext()); result = m_ShaderManager->RenderTextureShader(m_Direct3D->GetDeviceContext(), m_Model->GetIndexCount(), worldMatrix, viewMatrix, projectionMatrix, - m_Model->GetTexture(1)); + m_Model->GetTexture(0)); if (!result) { return false; @@ -745,7 +742,15 @@ bool ApplicationClass::Render(float rotation, float x, float y, float z, float t cube->Render(m_Direct3D->GetDeviceContext()); - result = m_ShaderManager->RenderlightShader(m_Direct3D->GetDeviceContext(), m_Model->GetIndexCount(), worldMatrix, viewMatrix, projectionMatrix, m_Model->GetTexture(0), + // render the texture using the texture shader. + result = m_ShaderManager->RenderTextureShader(m_Direct3D->GetDeviceContext(), cube->GetIndexCount(), worldMatrix, viewMatrix, projectionMatrix, cube->GetTexture(0)); + if (!result) + { + Logger::Get().Log("Could not render the cube model using the texture shader", __FILE__, __LINE__, Logger::LogLevel::Error); + return false; + } + + result = m_ShaderManager->RenderlightShader(m_Direct3D->GetDeviceContext(), m_Model->GetIndexCount(), worldMatrix, viewMatrix, projectionMatrix, cube->GetTexture(0), diffuseColor, lightPosition); if (!result) { @@ -769,6 +774,14 @@ bool ApplicationClass::Render(float rotation, float x, float y, float z, float t object->Render(m_Direct3D->GetDeviceContext()); + // render the texture using the texture shader. + result = m_ShaderManager->RenderTextureShader(m_Direct3D->GetDeviceContext(), object->GetIndexCount(), worldMatrix, viewMatrix, projectionMatrix, object->GetTexture(0)); + if (!result) + { + Logger::Get().Log("Could not render the cube model using the texture shader", __FILE__, __LINE__, Logger::LogLevel::Error); + return false; + } + result = m_ShaderManager->RenderlightShader(m_Direct3D->GetDeviceContext(), m_Model->GetIndexCount(), worldMatrix, viewMatrix, projectionMatrix, m_Model->GetTexture(0), diffuseColor, lightPosition); @@ -1474,60 +1487,4 @@ void ApplicationClass::SetLightPosition(int index, XMVECTOR position) //set the position m_Lights[index]->SetPosition(lightPosition.x, lightPosition.y, lightPosition.z); -} - -void ApplicationClass::DeleteLight(int index) -{ - Logger::Get().Log("Deleting light", __FILE__, __LINE__); - - if (index < 0 || index >= m_Lights.size()) - { - // Index out of bounds - return; - } - - // Delete the light object - delete m_Lights[index]; - - // Remove the light from the vector - m_Lights.erase(m_Lights.begin() + index); - - // Decrement the number of lights - m_numLights--; - - // Update the light position and color - for (int i = 0; i < m_numLights; i++) - { - m_Lights[i]->SetPosition(i * 10.0f, 10.0f, -10.0f); - m_Lights[i]->SetDiffuseColor(1.0f, 1.0f, 1.0f, 1.0f); - } -} - -void ApplicationClass::AddLight() -{ - Logger::Get().Log("Adding light", __FILE__, __LINE__); - - // Create a new light object - LightClass* newLight = new LightClass(); - - // Set the light position - newLight->SetPosition(m_numLights * 10.0f, 10.0f, -10.0f); - - // Set the light color - newLight->SetDiffuseColor(1.0f, 1.0f, 1.0f, 1.0f); - - // Set the light direction - newLight->SetDirection(0.0f, 0.0f, 1.0f); - - // Set the light specular color - newLight->SetSpecularColor(1.0f, 1.0f, 1.0f, 1.0f); - - // Set the light specular power - newLight->SetSpecularPower(16.0f); - - // Add the light to the vector - m_Lights.push_back(newLight); - - // Increment the number of lights - m_numLights++; -} +} \ No newline at end of file diff --git a/enginecustom/applicationclass.h b/enginecustom/applicationclass.h index c84c234..9308ff9 100644 --- a/enginecustom/applicationclass.h +++ b/enginecustom/applicationclass.h @@ -81,7 +81,6 @@ public: void DeleteLight(int index); void AddLight(); std::vector GetLights() const { return m_Lights; }; - bool GetShouldQuit() const { return m_ShouldQuit; }; void SetShouldQuit(bool shouldQuit) { m_ShouldQuit = shouldQuit; }; diff --git a/enginecustom/imgui.ini b/enginecustom/imgui.ini index 9936988..9e5e6dd 100644 --- a/enginecustom/imgui.ini +++ b/enginecustom/imgui.ini @@ -3,18 +3,18 @@ Pos=60,60 Size=400,400 [Window][Khaotic Engine] -Pos=30,173 +Pos=219,17 Size=392,218 [Window][Objects] -Pos=39,222 -Size=379,299 +Pos=916,66 +Size=637,299 [Window][Terrain] Pos=397,130 Size=342,82 [Window][Light] -Pos=34,172 +Pos=941,159 Size=345,230 diff --git a/enginecustom/imguiManager.cpp b/enginecustom/imguiManager.cpp index ab62194..be2fb45 100644 --- a/enginecustom/imguiManager.cpp +++ b/enginecustom/imguiManager.cpp @@ -15,12 +15,15 @@ bool imguiManager::Initialize(HWND hwnd, ID3D11Device* device, ID3D11DeviceConte { Logger::Get().Log("Initializing imgui", __FILE__, __LINE__); + m_device = device; + m_deviceContext = deviceContext; + IMGUI_CHECKVERSION(); ImGui::CreateContext(); io = &ImGui::GetIO(); ImGui_ImplWin32_Init(hwnd); - ImGui_ImplDX11_Init(device, deviceContext); + ImGui_ImplDX11_Init(m_device, m_deviceContext); ImGui::StyleColorsDark(); Logger::Get().Log("imgui initialized", __FILE__, __LINE__); @@ -88,7 +91,7 @@ void imguiManager::WidgetAddObject(ApplicationClass* app) ofn.lpstrFile = szFile; ofn.lpstrFile[0] = '\0'; ofn.nMaxFile = sizeof(szFile); - ofn.lpstrFilter = L"OBJ\0*.obj\0TXT\0*.txt\0KOBJ\0*.kobj"; + ofn.lpstrFilter = L"TXT\0*.txt\0KOBJ\0*.kobj\0*OBJ\0*.obj"; ofn.nFilterIndex = 1; ofn.lpstrFileTitle = NULL; ofn.nMaxFileTitle = 0; @@ -110,7 +113,6 @@ void imguiManager::WidgetObjectWindow(ApplicationClass* app) { ImGui::Begin("Objects", &showObjectWindow); int index = 0; - int count = 0; for (auto& object : app->GetKobjects()) { std::string headerName = object->GetName() + " " + std::to_string(index); @@ -145,17 +147,80 @@ void imguiManager::WidgetObjectWindow(ApplicationClass* app) ImGui::Separator(); // Texture - std::string textureLabel = "Texture##" + std::to_string(index); - ID3D11ShaderResourceView* texture = object->GetTexture(0); - if (texture != nullptr) + // add all texture category names to a vector + std::vector textureCategories = { "Diffuse", "Normal", "Specular", "Alpha", "Light", "Change Me" }; + + + for (int count = 0; count < 6; count++) { - if (ImGui::ImageButton((ImTextureID)texture, ImVec2(64, 64))) + std::string textureLabel = "Texture##" + std::to_string(index); + ID3D11ShaderResourceView* texture = object->GetTexture(count); + if (texture != nullptr) { - count++; - + // Set the cursor position + ImGui::SetCursorPosX(count * (64 + 20) + 10); // 64 is the width of the image, 10 is the spacing + + // Display the texture name + std::string textureName = textureCategories[count]; + ImGui::Text(textureName.c_str()); + + if(count < 5) + { + ImGui::SameLine(); + } } } - ImGui::Text("Texture count: %d", count); + + // Display all images + for (int count = 0; count < 6; count++) + { + std::string textureLabel = "Texture##" + std::to_string(index); + ID3D11ShaderResourceView* texture = object->GetTexture(count); + if (texture != nullptr) + { + // Set the cursor position + ImGui::SetCursorPosX(count * (64 + 20) + 10); // 64 is the width of the image, 10 is the spacing + + if (ImGui::ImageButton((ImTextureID)texture, ImVec2(64, 64))) + { + // Open file dialog + OPENFILENAME ofn; + WCHAR szFile[260]; + ZeroMemory(&ofn, sizeof(ofn)); + ofn.lStructSize = sizeof(ofn); + ofn.hwndOwner = NULL; + ofn.lpstrFile = szFile; + ofn.lpstrFile[0] = '\0'; + ofn.nMaxFile = sizeof(szFile); + ofn.lpstrFilter = L"Texture\0*.tga\0"; + ofn.nFilterIndex = 1; + ofn.lpstrFileTitle = NULL; + ofn.nMaxFileTitle = 0; + ofn.lpstrInitialDir = NULL; + ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST; + + if (GetOpenFileName(&ofn)) + { + // Load the selected texture + object->ChangeTexture(m_device, m_deviceContext, ofn.lpstrFile, index); + } + } + + if (ImGui::IsItemHovered()) + { + ImGui::BeginTooltip(); + ImGui::Image((ImTextureID)texture, ImVec2(256, 256)); + ImGui::EndTooltip(); + } + + // If this is not the last texture, put the next button on the same line + if (count < 5) + { + ImGui::SameLine(); + } + } + } + ImGui::Separator(); @@ -261,11 +326,6 @@ void imguiManager::WidgetLightWindow(ApplicationClass* app) { ImGui::Begin("Light", &showLightWindow); int index = 0; - // add light button - if (ImGui::Button("Add Light")) - { - app->AddLight(); - } for(auto& light : app->GetLights()) { @@ -291,15 +351,6 @@ void imguiManager::WidgetLightWindow(ApplicationClass* app) } ImGui::Separator(); - - // Delete button - std::string deleteLabel = "Delete##" + std::to_string(index); - if (ImGui::Button(deleteLabel.c_str())) - { - app->DeleteLight(index); - } - - ImGui::Separator(); } index++; }; diff --git a/enginecustom/imguiManager.h b/enginecustom/imguiManager.h index 0d8ef86..c814210 100644 --- a/enginecustom/imguiManager.h +++ b/enginecustom/imguiManager.h @@ -42,6 +42,9 @@ private : private: ImGuiIO* io; + ID3D11Device* m_device; + ID3D11DeviceContext* m_deviceContext; + }; #endif \ No newline at end of file diff --git a/enginecustom/lightshaderclass.cpp b/enginecustom/lightshaderclass.cpp index 9c483e2..e137cd9 100644 --- a/enginecustom/lightshaderclass.cpp +++ b/enginecustom/lightshaderclass.cpp @@ -475,10 +475,10 @@ bool LightShaderClass::SetShaderParameters(ID3D11DeviceContext* deviceContext, X dataPtr2 = (LightPositionBufferType*)mappedResource.pData; // Copy the light position variables into the constant buffer. - dataPtr2->lightPosition[0] = lightPosition[0]; - dataPtr2->lightPosition[1] = lightPosition[1]; - dataPtr2->lightPosition[2] = lightPosition[2]; - dataPtr2->lightPosition[3] = lightPosition[3]; + for (int i = 0; i < NUM_LIGHTS; i++) + { + dataPtr2->lightPosition[i] = lightPosition[i]; + } // Unlock the constant buffer. deviceContext->Unmap(m_lightPositionBuffer, 0); @@ -504,10 +504,10 @@ bool LightShaderClass::SetShaderParameters(ID3D11DeviceContext* deviceContext, X dataPtr3 = (LightColorBufferType*)mappedResource.pData; // Copy the light color variables into the constant buffer. - dataPtr3->diffuseColor[0] = diffuseColor[0]; - dataPtr3->diffuseColor[1] = diffuseColor[1]; - dataPtr3->diffuseColor[2] = diffuseColor[2]; - dataPtr3->diffuseColor[3] = diffuseColor[3]; + for (int i = 0; i < NUM_LIGHTS; i++) + { + dataPtr3->diffuseColor[i] = diffuseColor[i]; + } // Unlock the constant buffer. deviceContext->Unmap(m_lightColorBuffer, 0); diff --git a/enginecustom/lightshaderclass.h b/enginecustom/lightshaderclass.h index eb32019..3cf4974 100644 --- a/enginecustom/lightshaderclass.h +++ b/enginecustom/lightshaderclass.h @@ -9,7 +9,6 @@ // GLOBALS // ///////////// const int NUM_LIGHTS = 4; - ////////////// // INCLUDES // ////////////// @@ -88,7 +87,6 @@ private: ID3D11Buffer* m_lightBuffer; ID3D11Buffer* m_lightColorBuffer; ID3D11Buffer* m_lightPositionBuffer; - }; #endif \ No newline at end of file diff --git a/enginecustom/modelclass.cpp b/enginecustom/modelclass.cpp index 5ff2364..650fe4a 100644 --- a/enginecustom/modelclass.cpp +++ b/enginecustom/modelclass.cpp @@ -399,7 +399,6 @@ void ModelClass::CalculateModelVectors() void ModelClass::CalculateTangentBinormal(TempVertexType vertex1, TempVertexType vertex2, TempVertexType vertex3, VectorType& tangent, VectorType& binormal) { - Logger::Get().Log("Calculating tangent and binormal", __FILE__, __LINE__); float vector1[3], vector2[3]; float tuVector[2], tvVector[2]; @@ -451,8 +450,6 @@ void ModelClass::CalculateTangentBinormal(TempVertexType vertex1, TempVertexType binormal.y = binormal.y / length; binormal.z = binormal.z / length; - Logger::Get().Log("Tangent and binormal calculated", __FILE__, __LINE__); - return; } @@ -469,4 +466,27 @@ void ModelClass::ReleaseModel() Logger::Get().Log("Model released", __FILE__, __LINE__); return; +} + +bool ModelClass::ChangeTexture(ID3D11Device* device, ID3D11DeviceContext* deviceContext, std::wstring filename, int index) +{ + bool result; + + // convert wstring to string + std::string str(filename.begin(), filename.end()); + + // Release the old texture object. + m_Textures[index].Shutdown(); + + // Initialize the new texture object. + result = m_Textures[index].Initialize(device, deviceContext, str); + if (!result) + { + Logger::Get().Log("Failed to initialize texture", __FILE__, __LINE__, Logger::LogLevel::Error); + return false; + } + + Logger::Get().Log("Texture changed", __FILE__, __LINE__); + + return true; } \ No newline at end of file diff --git a/enginecustom/modelclass.h b/enginecustom/modelclass.h index ba6cca1..d32de0b 100644 --- a/enginecustom/modelclass.h +++ b/enginecustom/modelclass.h @@ -88,6 +88,7 @@ public: int GetIndexCount(); ID3D11ShaderResourceView* GetTexture(int); + bool ChangeTexture(ID3D11Device*, ID3D11DeviceContext*, std::wstring filename, int index); private: bool InitializeBuffers(ID3D11Device*); diff --git a/enginecustom/shadermanagerclass.h b/enginecustom/shadermanagerclass.h index 891085d..7910b2d 100644 --- a/enginecustom/shadermanagerclass.h +++ b/enginecustom/shadermanagerclass.h @@ -13,8 +13,6 @@ #include "transparentshaderclass.h" #include "lightshaderclass.h" #include "lightmapshaderclass.h" - - //////////////////////////////////////////////////////////////////////////////// // Class name: ShaderManagerClass ////////////////////////////////////////////////////////////////////////////////