Skip to content

Commit

Permalink
Add generic GL dialog and canvas classes
Browse files Browse the repository at this point in the history
GLCanvas, GLDialog
  • Loading branch information
ousnius committed Apr 3, 2022
1 parent 3905f4b commit ed7611d
Show file tree
Hide file tree
Showing 7 changed files with 321 additions and 0 deletions.
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ set(commonsources
src/render/GLOffscreenBuffer.cpp
src/render/GLShader.cpp
src/render/GLSurface.cpp
src/render/GLCanvas.cpp
src/render/GLDialog.cpp
src/ui/wxStateButton.cpp
src/ui/wxSliderPanel.cpp
src/utils/AABBTree.cpp
Expand Down
4 changes: 4 additions & 0 deletions OutfitStudio.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -808,6 +808,8 @@
<ClInclude Include="src\program\PresetSaveDialog.h" />
<ClInclude Include="src\program\ShapeProperties.h" />
<ClInclude Include="src\program\SliderDataImportDialog.h" />
<ClInclude Include="src\render\GLCanvas.h" />
<ClInclude Include="src\render\GLDialog.h" />
<ClInclude Include="src\render\GLExtensions.h" />
<ClInclude Include="src\render\GLMaterial.h" />
<ClInclude Include="src\render\GLOffscreenBuffer.h" />
Expand Down Expand Up @@ -881,6 +883,8 @@
<ClCompile Include="src\program\PresetSaveDialog.cpp" />
<ClCompile Include="src\program\ShapeProperties.cpp" />
<ClCompile Include="src\program\SliderDataImportDialog.cpp" />
<ClCompile Include="src\render\GLCanvas.cpp" />
<ClCompile Include="src\render\GLDialog.cpp" />
<ClCompile Include="src\render\GLExtensions.cpp" />
<ClCompile Include="src\render\GLMaterial.cpp" />
<ClCompile Include="src\render\GLOffscreenBuffer.cpp" />
Expand Down
12 changes: 12 additions & 0 deletions OutfitStudio.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -1888,6 +1888,12 @@
<ClInclude Include="lib\gli\glm\simd\neon.h">
<Filter>Libraries\gli\glm\simd</Filter>
</ClInclude>
<ClInclude Include="src\render\GLCanvas.h">
<Filter>Rendering</Filter>
</ClInclude>
<ClInclude Include="src\render\GLDialog.h">
<Filter>Rendering</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="lib\TinyXML-2\tinyxml2.cpp">
Expand Down Expand Up @@ -2100,6 +2106,12 @@
<ClCompile Include="lib\SOIL2\wfETC.c">
<Filter>Libraries\SOIL2</Filter>
</ClCompile>
<ClCompile Include="src\render\GLDialog.cpp">
<Filter>Rendering</Filter>
</ClCompile>
<ClCompile Include="src\render\GLCanvas.cpp">
<Filter>Rendering</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Xml Include="Config.xml">
Expand Down
76 changes: 76 additions & 0 deletions src/render/GLCanvas.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
BodySlide and Outfit Studio
See the included LICENSE file
*/

#include "GLDialog.h"

wxBEGIN_EVENT_TABLE(GLCanvas, wxGLCanvas)
EVT_PAINT(GLCanvas::OnPaint)
EVT_MOTION(GLCanvas::OnMotion)
EVT_MOUSEWHEEL(GLCanvas::OnMouseWheel)
EVT_KEY_UP(GLCanvas::OnKeyUp)
EVT_SIZE(GLCanvas::OnResized)
wxEND_EVENT_TABLE()

GLCanvas::GLCanvas(GLDialog* parent, const wxGLAttributes& attribs)
: wxGLCanvas(parent, attribs, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxFULL_REPAINT_ON_RESIZE)
, parent(parent) {}

void GLCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) {
// Initialize OpenGL the first time the window is painted.
// We unfortunately can't initialize it before the window is shown.
// We could register for the EVT_SHOW event, but unfortunately it
// appears to only be called after the first few EVT_PAINT events.
// It also isn't supported on all platforms.
if (firstPaint) {
firstPaint = false;
parent->OnShown();
}
parent->Render();
}

void GLCanvas::OnMotion(wxMouseEvent& event) {
if (parent->IsActive())
SetFocus();

auto delta = event.GetPosition() - lastMousePosition;

if (event.LeftIsDown()) {
parent->LeftDrag(delta.x, delta.y);
}
else if (event.MiddleIsDown()) {
if (wxGetKeyState(WXK_SHIFT))
parent->MouseWheel(delta.y);
else
parent->LeftDrag(delta.x, delta.y);
}
else if (event.RightIsDown()) {
if (wxGetKeyState(WXK_SHIFT))
parent->LeftDrag(delta.x, delta.y);
else
parent->RightDrag(delta.x, delta.y);
}
else
parent->TrackMouse(event.GetX(), event.GetY());

lastMousePosition = event.GetPosition();
}

void GLCanvas::OnMouseWheel(wxMouseEvent& event) {
parent->MouseWheel(event.GetWheelRotation());
}

void GLCanvas::OnKeyUp(wxKeyEvent& event) {
int key = event.GetKeyCode();
switch (key) {
case 'T': parent->ToggleTextures(); break;
case 'W': parent->ToggleWireframe(); break;
case 'L': parent->ToggleLighting(); break;
case 'G': parent->ToggleFloor(); break;
}
}

void GLCanvas::OnResized(wxSizeEvent& event) {
parent->Resized(event.GetSize().GetWidth(), event.GetSize().GetHeight());
}
28 changes: 28 additions & 0 deletions src/render/GLCanvas.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
BodySlide and Outfit Studio
See the included LICENSE file
*/

#pragma once

#include <wx/wx.h>
#include <wx/glcanvas.h>

class GLDialog;

class GLCanvas : public wxGLCanvas {
GLDialog* parent = nullptr;
bool firstPaint = true;
wxPoint lastMousePosition;

public:
GLCanvas(GLDialog* parent, const wxGLAttributes& attribs);

void OnPaint(wxPaintEvent& event);
void OnMotion(wxMouseEvent& event);
void OnMouseWheel(wxMouseEvent& event);
void OnKeyUp(wxKeyEvent& event);
void OnResized(wxSizeEvent& event);

wxDECLARE_EVENT_TABLE();
};
150 changes: 150 additions & 0 deletions src/render/GLDialog.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
/*
BodySlide and Outfit Studio
See the included LICENSE file
*/

#include "GLDialog.h"
#include "../utils/ConfigurationManager.h"

using namespace nifly;

extern ConfigurationManager Config;


wxBEGIN_EVENT_TABLE(GLDialog, wxDialog)
EVT_CLOSE(GLDialog::OnClose)
wxEND_EVENT_TABLE()

GLDialog::GLDialog() {
CenterOnParent();
SetDoubleBuffered(true);
}

GLCanvas* GLDialog::CreateCanvas() {
canvas = new GLCanvas(this, GLSurface::GetGLAttribs());
context = std::make_unique<wxGLContext>(canvas, nullptr, &GLSurface::GetGLContextAttribs());
return canvas;
}

void GLDialog::OnShown() {
if (!context->IsOK()) {
Destroy();
canvas = nullptr;
wxLogError("Preview failed: OpenGL context is not OK.");
wxMessageBox(_("Preview failed: OpenGL context is not OK."), _("OpenGL Error"), wxICON_ERROR);
return;
}

gls.Initialize(canvas, context.get());

auto size = canvas->GetSize();
gls.SetStartingView(Vector3(0.0f, -5.0f, -15.0f), Vector3(15.0f, 0.0f, 0.0f), size.GetWidth(), size.GetHeight(), 65.0);

int ambient = Config.GetIntValue("Lights/Ambient");
int frontal = Config.GetIntValue("Lights/Frontal");

int directional0 = Config.GetIntValue("Lights/Directional0");
int directional0X = Config.GetIntValue("Lights/Directional0.x");
int directional0Y = Config.GetIntValue("Lights/Directional0.y");
int directional0Z = Config.GetIntValue("Lights/Directional0.z");

int directional1 = Config.GetIntValue("Lights/Directional1");
int directional1X = Config.GetIntValue("Lights/Directional1.x");
int directional1Y = Config.GetIntValue("Lights/Directional1.y");
int directional1Z = Config.GetIntValue("Lights/Directional1.z");

int directional2 = Config.GetIntValue("Lights/Directional2");
int directional2X = Config.GetIntValue("Lights/Directional2.x");
int directional2Y = Config.GetIntValue("Lights/Directional2.y");
int directional2Z = Config.GetIntValue("Lights/Directional2.z");

Vector3 directional0Dir = Vector3(directional0X / 100.0f, directional0Y / 100.0f, directional0Z / 100.0f);
Vector3 directional1Dir = Vector3(directional1X / 100.0f, directional1Y / 100.0f, directional1Z / 100.0f);
Vector3 directional2Dir = Vector3(directional2X / 100.0f, directional2Y / 100.0f, directional2Z / 100.0f);
gls.UpdateLights(ambient, frontal, directional0, directional1, directional2, directional0Dir, directional1Dir, directional2Dir);

if (Config.Exists("Rendering/ColorBackground")) {
int colorBackgroundR = Config.GetIntValue("Rendering/ColorBackground.r");
int colorBackgroundG = Config.GetIntValue("Rendering/ColorBackground.g");
int colorBackgroundB = Config.GetIntValue("Rendering/ColorBackground.b");
gls.SetBackgroundColor(Vector3(colorBackgroundR / 255.0f, colorBackgroundG / 255.0f, colorBackgroundB / 255.0f));
}

if (Config.Exists("Rendering/ColorWire")) {
int colorWireR = Config.GetIntValue("Rendering/ColorWire.r");
int colorWireG = Config.GetIntValue("Rendering/ColorWire.g");
int colorWireB = Config.GetIntValue("Rendering/ColorWire.b");
gls.SetWireColor(Vector3(colorWireR / 255.0f, colorWireG / 255.0f, colorWireB / 255.0f));
}

// Turn texture rendering off initially
gls.ToggleTextures();

floorMeshes = gls.AddFloor();
}

void GLDialog::OnClose(wxCloseEvent& WXUNUSED(event)) {
Cleanup();
Destroy();
}

void GLDialog::LeftDrag(int dX, int dY) {
gls.PanCamera(dX, dY);
gls.RenderOneFrame();
}

void GLDialog::RightDrag(int dX, int dY) {
gls.TurnTableCamera(dX);
gls.PitchCamera(dY);
gls.RenderOneFrame();
}

void GLDialog::MouseWheel(int dW) {
gls.DollyCamera(dW);
gls.RenderOneFrame();
}

void GLDialog::TrackMouse(int X, int Y) {
gls.UpdateCursor(X, Y);
gls.RenderOneFrame();
}

void GLDialog::ToggleTextures() {
gls.ToggleTextures();
gls.RenderOneFrame();
}

void GLDialog::ToggleWireframe() {
gls.ToggleWireframe();
gls.RenderOneFrame();
}

void GLDialog::ToggleLighting() {
gls.ToggleLighting();
gls.RenderOneFrame();
}

void GLDialog::ToggleFloor() {
for (auto& m : floorMeshes)
m->bVisible = !m->bVisible;

gls.RenderOneFrame();
}

void GLDialog::Render() {
gls.RenderOneFrame();
}

void GLDialog::Resized(uint32_t w, uint32_t h) {
gls.SetSize(w, h);
}

void GLDialog::Cleanup() {
// Set current context for resource deletion
if (canvas && context)
canvas->SetCurrent(*context);

gls.Cleanup();
shapeMaterials.clear();
gls.RenderOneFrame();
}
49 changes: 49 additions & 0 deletions src/render/GLDialog.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
BodySlide and Outfit Studio
See the included LICENSE file
*/

#pragma once

#include <wx/wx.h>

#include "GLCanvas.h"
#include "GLSurface.h"

class GLDialog : public wxDialog {
public:
GLDialog();

GLCanvas* CreateCanvas();
virtual void OnShown();
void OnClose(wxCloseEvent& event);

GLCanvas* GetCanvas() { return canvas; }
wxGLContext* GetContext() { return context.get(); }
GLSurface& GetSurface() { return gls; }

void LeftDrag(int dX, int dY);
void RightDrag(int dX, int dY);
void MouseWheel(int dW);
void TrackMouse(int X, int Y);

void ToggleTextures();
void ToggleWireframe();
void ToggleLighting();
void ToggleFloor();

void Resized(uint32_t w, uint32_t h);

void Render();
void Cleanup();

wxDECLARE_EVENT_TABLE();

private:
GLCanvas* canvas = nullptr;
std::unique_ptr<wxGLContext> context;
GLSurface gls;

std::unordered_map<std::string, GLMaterial*> shapeMaterials;
std::vector<Mesh*> floorMeshes;
};

0 comments on commit ed7611d

Please sign in to comment.