Files
WickedEngine/Editor/ObjectWindow.cpp
T
2018-12-09 17:51:17 +00:00

563 lines
19 KiB
C++

#include "stdafx.h"
#include "ObjectWindow.h"
#include "wiSceneSystem.h"
#include "xatlas.h"
#include <sstream>
using namespace wiECS;
using namespace wiSceneSystem;
static void SetPixel(uint8_t *dest, int destWidth, int x, int y, const uint8_t *color)
{
uint8_t *pixel = &dest[x * 4 + y * (destWidth * 4)];
pixel[0] = color[0];
pixel[1] = color[1];
pixel[2] = color[2];
pixel[3] = color[3];
}
// https://github.com/miloyip/line/blob/master/line_bresenham.c
static void RasterizeLine(uint8_t *dest, int destWidth, const int *p1, const int *p2, const uint8_t *color)
{
const int dx = abs(p2[0] - p1[0]), sx = p1[0] < p2[0] ? 1 : -1;
const int dy = abs(p2[1] - p1[1]), sy = p1[1] < p2[1] ? 1 : -1;
int err = (dx > dy ? dx : -dy) / 2;
int current[2];
current[0] = p1[0];
current[1] = p1[1];
while (SetPixel(dest, destWidth, current[0], current[1], color), current[0] != p2[0] || current[1] != p2[1])
{
const int e2 = err;
if (e2 > -dx) { err -= dy; current[0] += sx; }
if (e2 < dy) { err += dx; current[1] += sy; }
}
}
// https://github.com/ssloy/tinyrenderer/wiki/Lesson-2:-Triangle-rasterization-and-back-face-culling
static void RasterizeTriangle(uint8_t *dest, int destWidth, const int *t0, const int *t1, const int *t2, const uint8_t *color)
{
if (t0[1] > t1[1]) std::swap(t0, t1);
if (t0[1] > t2[1]) std::swap(t0, t2);
if (t1[1] > t2[1]) std::swap(t1, t2);
int total_height = t2[1] - t0[1];
for (int i = 0; i < total_height; i++) {
bool second_half = i > t1[1] - t0[1] || t1[1] == t0[1];
int segment_height = second_half ? t2[1] - t1[1] : t1[1] - t0[1];
float alpha = (float)i / total_height;
float beta = (float)(i - (second_half ? t1[1] - t0[1] : 0)) / segment_height;
int A[2], B[2];
for (int j = 0; j < 2; j++) {
A[j] = int(t0[j] + (t2[j] - t0[j]) * alpha);
B[j] = int(second_half ? t1[j] + (t2[j] - t1[j]) * beta : t0[j] + (t1[j] - t0[j]) * beta);
}
if (A[0] > B[0]) std::swap(A, B);
for (int j = A[0]; j <= B[0]; j++)
SetPixel(dest, destWidth, j, t0[1] + i, color);
}
}
ObjectWindow::ObjectWindow(wiGUI* gui) : GUI(gui)
{
assert(GUI && "Invalid GUI!");
float screenW = (float)wiRenderer::GetDevice()->GetScreenWidth();
float screenH = (float)wiRenderer::GetDevice()->GetScreenHeight();
objectWindow = new wiWindow(GUI, "Object Window");
objectWindow->SetSize(XMFLOAT2(600, 520));
objectWindow->SetEnabled(false);
GUI->AddWidget(objectWindow);
float x = 450;
float y = 0;
nameLabel = new wiLabel("NAMELABEL");
nameLabel->SetText("");
nameLabel->SetPos(XMFLOAT2(x - 30, y += 30));
nameLabel->SetSize(XMFLOAT2(150, 20));
objectWindow->AddWidget(nameLabel);
renderableCheckBox = new wiCheckBox("Renderable: ");
renderableCheckBox->SetTooltip("Set object to be participating in rendering.");
renderableCheckBox->SetPos(XMFLOAT2(x, y += 30));
renderableCheckBox->SetCheck(true);
renderableCheckBox->OnClick([&](wiEventArgs args) {
ObjectComponent* object = wiRenderer::GetScene().objects.GetComponent(entity);
if (object != nullptr)
{
object->SetRenderable(args.bValue);
}
});
objectWindow->AddWidget(renderableCheckBox);
ditherSlider = new wiSlider(0, 1, 0, 1000, "Dither: ");
ditherSlider->SetTooltip("Adjust dithered transparency of the object. This disables some optimizations so performance can be affected.");
ditherSlider->SetSize(XMFLOAT2(100, 30));
ditherSlider->SetPos(XMFLOAT2(x, y += 30));
ditherSlider->OnSlide([&](wiEventArgs args) {
ObjectComponent* object = wiRenderer::GetScene().objects.GetComponent(entity);
if (object != nullptr)
{
object->color.w = 1 - args.fValue;
}
});
objectWindow->AddWidget(ditherSlider);
cascadeMaskSlider = new wiSlider(0, 3, 0, 3, "Cascade Mask: ");
cascadeMaskSlider->SetTooltip("How many shadow cascades to skip when rendering this object into shadow maps? (0: skip none, it will be in all cascades, 1: skip first (biggest cascade), ...etc...");
cascadeMaskSlider->SetSize(XMFLOAT2(100, 30));
cascadeMaskSlider->SetPos(XMFLOAT2(x, y += 30));
cascadeMaskSlider->OnSlide([&](wiEventArgs args) {
ObjectComponent* object = wiRenderer::GetScene().objects.GetComponent(entity);
if (object != nullptr)
{
object->cascadeMask = (uint32_t)args.iValue;
}
});
objectWindow->AddWidget(cascadeMaskSlider);
colorPicker = new wiColorPicker(GUI, "Object Color");
colorPicker->SetPos(XMFLOAT2(10, 30));
colorPicker->RemoveWidgets();
colorPicker->SetVisible(true);
colorPicker->SetEnabled(true);
colorPicker->OnColorChanged([&](wiEventArgs args) {
ObjectComponent* object = wiRenderer::GetScene().objects.GetComponent(entity);
if (object != nullptr)
{
XMFLOAT3 col = args.color.toFloat3();
object->color = XMFLOAT4(powf(col.x, 1.f / 2.2f), powf(col.y, 1.f / 2.2f), powf(col.z, 1.f / 2.2f), object->color.w);
}
});
objectWindow->AddWidget(colorPicker);
y += 60;
physicsLabel = new wiLabel("PHYSICSLABEL");
physicsLabel->SetText("PHYSICS SETTINGS");
physicsLabel->SetPos(XMFLOAT2(x - 30, y += 30));
physicsLabel->SetSize(XMFLOAT2(150, 20));
objectWindow->AddWidget(physicsLabel);
rigidBodyCheckBox = new wiCheckBox("Rigid Body Physics: ");
rigidBodyCheckBox->SetTooltip("Enable rigid body physics simulation.");
rigidBodyCheckBox->SetPos(XMFLOAT2(x, y += 30));
rigidBodyCheckBox->SetCheck(false);
rigidBodyCheckBox->OnClick([&](wiEventArgs args)
{
Scene& scene = wiRenderer::GetScene();
RigidBodyPhysicsComponent* physicscomponent = scene.rigidbodies.GetComponent(entity);
if (args.bValue)
{
if (physicscomponent == nullptr)
{
RigidBodyPhysicsComponent& rigidbody = scene.rigidbodies.Create(entity);
rigidbody.SetKinematic(kinematicCheckBox->GetCheck());
rigidbody.SetDisableDeactivation(disabledeactivationCheckBox->GetCheck());
rigidbody.shape = (RigidBodyPhysicsComponent::CollisionShape)collisionShapeComboBox->GetSelected();
}
}
else
{
if (physicscomponent != nullptr)
{
scene.rigidbodies.Remove(entity);
}
}
});
objectWindow->AddWidget(rigidBodyCheckBox);
kinematicCheckBox = new wiCheckBox("Kinematic: ");
kinematicCheckBox->SetTooltip("Toggle kinematic behaviour.");
kinematicCheckBox->SetPos(XMFLOAT2(x, y += 30));
kinematicCheckBox->SetCheck(false);
kinematicCheckBox->OnClick([&](wiEventArgs args) {
RigidBodyPhysicsComponent* physicscomponent = wiRenderer::GetScene().rigidbodies.GetComponent(entity);
if (physicscomponent != nullptr)
{
physicscomponent->SetKinematic(args.bValue);
}
});
objectWindow->AddWidget(kinematicCheckBox);
disabledeactivationCheckBox = new wiCheckBox("Disable Deactivation: ");
disabledeactivationCheckBox->SetTooltip("Toggle kinematic behaviour.");
disabledeactivationCheckBox->SetPos(XMFLOAT2(x, y += 30));
disabledeactivationCheckBox->SetCheck(false);
disabledeactivationCheckBox->OnClick([&](wiEventArgs args) {
RigidBodyPhysicsComponent* physicscomponent = wiRenderer::GetScene().rigidbodies.GetComponent(entity);
if (physicscomponent != nullptr)
{
physicscomponent->SetDisableDeactivation(args.bValue);
}
});
objectWindow->AddWidget(disabledeactivationCheckBox);
collisionShapeComboBox = new wiComboBox("Collision Shape:");
collisionShapeComboBox->SetSize(XMFLOAT2(100, 20));
collisionShapeComboBox->SetPos(XMFLOAT2(x, y += 30));
collisionShapeComboBox->AddItem("Box");
collisionShapeComboBox->AddItem("Sphere");
collisionShapeComboBox->AddItem("Capsule");
collisionShapeComboBox->AddItem("Convex Hull");
collisionShapeComboBox->AddItem("Triangle Mesh");
collisionShapeComboBox->OnSelect([&](wiEventArgs args)
{
RigidBodyPhysicsComponent* physicscomponent = wiRenderer::GetScene().rigidbodies.GetComponent(entity);
if (physicscomponent != nullptr)
{
switch (args.iValue)
{
case 0:
physicscomponent->shape = RigidBodyPhysicsComponent::CollisionShape::BOX;
break;
case 1:
physicscomponent->shape = RigidBodyPhysicsComponent::CollisionShape::SPHERE;
break;
case 2:
physicscomponent->shape = RigidBodyPhysicsComponent::CollisionShape::CAPSULE;
break;
case 3:
physicscomponent->shape = RigidBodyPhysicsComponent::CollisionShape::CONVEX_HULL;
break;
case 4:
physicscomponent->shape = RigidBodyPhysicsComponent::CollisionShape::TRIANGLE_MESH;
break;
default:
break;
}
}
});
collisionShapeComboBox->SetSelected(0);
collisionShapeComboBox->SetEnabled(true);
collisionShapeComboBox->SetTooltip("Set rigid body collision shape.");
objectWindow->AddWidget(collisionShapeComboBox);
y += 30;
lightmapResolutionSlider = new wiSlider(32, 1024, 128, 1024 - 32, "Lightmap resolution: ");
lightmapResolutionSlider->SetTooltip("Set the approximate resolution for this object's lightmap. This will be packed into the larger global lightmap later.");
lightmapResolutionSlider->SetSize(XMFLOAT2(100, 30));
lightmapResolutionSlider->SetPos(XMFLOAT2(x, y += 30));
lightmapResolutionSlider->OnSlide([&](wiEventArgs args) {
// unfortunately, we must be pow2 with full float lightmap format, otherwise it could be unlimited (but accumulation blending would suffer then)
// or at least for me, downloading the lightmap was glitching out when non-pow 2 and RGBA32_FLOAT format
lightmapResolutionSlider->SetValue(float(wiMath::GetNextPowerOfTwo(uint32_t(args.fValue))));
});
objectWindow->AddWidget(lightmapResolutionSlider);
generateLightmapButton = new wiButton("Generate Lightmap");
generateLightmapButton->SetTooltip("Render the lightmap for only this object. It will automatically combined with the global lightmap.");
generateLightmapButton->SetPos(XMFLOAT2(x, y += 30));
generateLightmapButton->SetSize(XMFLOAT2(140,30));
generateLightmapButton->OnClick([&](wiEventArgs args) {
Scene& scene = wiRenderer::GetScene();
ObjectComponent* objectcomponent = scene.objects.GetComponent(entity);
if (objectcomponent != nullptr)
{
MeshComponent* meshcomponent = scene.meshes.GetComponent(objectcomponent->meshID);
if (meshcomponent != nullptr)
{
objectcomponent->ClearLightmap();
xatlas::Atlas* atlas = xatlas::Create();
// Prepare mesh to be processed by xatlas:
{
xatlas::InputMesh mesh;
mesh.vertexCount = (int)meshcomponent->vertex_positions.size();
mesh.vertexPositionData = meshcomponent->vertex_positions.data();
mesh.vertexPositionStride = sizeof(float) * 3;
if (!meshcomponent->vertex_normals.empty()) {
mesh.vertexNormalData = meshcomponent->vertex_normals.data();
mesh.vertexNormalStride = sizeof(float) * 3;
}
if (!meshcomponent->vertex_texcoords.empty()) {
mesh.vertexUvData = meshcomponent->vertex_texcoords.data();
mesh.vertexUvStride = sizeof(float) * 2;
}
mesh.indexCount = (int)meshcomponent->indices.size();
mesh.indexData = meshcomponent->indices.data();
mesh.indexFormat = xatlas::IndexFormat::UInt32;
xatlas::AddMeshError::Enum error = xatlas::AddMesh(atlas, mesh);
if (error != xatlas::AddMeshError::Success) {
wiHelper::messageBox(xatlas::StringForEnum(error), "Adding mesh to xatlas failed!");
return;
}
}
// Generate atlas:
{
xatlas::PackerOptions packerOptions;
packerOptions.resolution = (uint32_t)lightmapResolutionSlider->GetValue();
packerOptions.conservative = true;
packerOptions.padding = 1;
xatlas::GenerateCharts(atlas, xatlas::CharterOptions(), nullptr, nullptr);
xatlas::PackCharts(atlas, packerOptions, nullptr, nullptr);
const uint32_t charts = xatlas::GetNumCharts(atlas);
objectcomponent->lightmapWidth = xatlas::GetWidth(atlas);
objectcomponent->lightmapHeight = xatlas::GetHeight(atlas);
const xatlas::OutputMesh* mesh = xatlas::GetOutputMeshes(atlas)[0];
// Note: we must recreate all vertex buffers, because the index buffer will be different (the atlas could have removed shared vertices)
meshcomponent->indices.clear();
meshcomponent->indices.resize(mesh->indexCount);
std::vector<XMFLOAT3> positions(mesh->vertexCount);
std::vector<XMFLOAT2> atlas(mesh->vertexCount);
std::vector<XMFLOAT3> normals;
std::vector<XMFLOAT2> texcoords;
std::vector<uint32_t> colors;
std::vector<XMUINT4> boneindices;
std::vector<XMFLOAT4> boneweights;
if (!meshcomponent->vertex_normals.empty())
{
normals.resize(mesh->vertexCount);
}
if (!meshcomponent->vertex_texcoords.empty())
{
texcoords.resize(mesh->vertexCount);
}
if (!meshcomponent->vertex_colors.empty())
{
colors.resize(mesh->vertexCount);
}
if (!meshcomponent->vertex_boneindices.empty())
{
boneindices.resize(mesh->vertexCount);
}
if (!meshcomponent->vertex_boneweights.empty())
{
boneweights.resize(mesh->vertexCount);
}
for (uint32_t j = 0; j < mesh->indexCount; ++j)
{
const uint32_t ind = mesh->indexArray[j];
const xatlas::OutputVertex &v = mesh->vertexArray[ind];
meshcomponent->indices[j] = ind;
atlas[ind].x = v.uv[0] / float(objectcomponent->lightmapWidth);
atlas[ind].y = v.uv[1] / float(objectcomponent->lightmapHeight);
positions[ind] = meshcomponent->vertex_positions[v.xref];
if (!normals.empty())
{
normals[ind] = meshcomponent->vertex_normals[v.xref];
}
if (!texcoords.empty())
{
texcoords[ind] = meshcomponent->vertex_texcoords[v.xref];
}
if (!colors.empty())
{
colors[ind] = meshcomponent->vertex_colors[v.xref];
}
if (!boneindices.empty())
{
boneindices[ind] = meshcomponent->vertex_boneindices[v.xref];
}
if (!boneweights.empty())
{
boneweights[ind] = meshcomponent->vertex_boneweights[v.xref];
}
}
meshcomponent->vertex_positions = positions;
meshcomponent->vertex_atlas = atlas;
if (!normals.empty())
{
meshcomponent->vertex_normals = normals;
}
if (!texcoords.empty())
{
meshcomponent->vertex_texcoords = texcoords;
}
if (!colors.empty())
{
meshcomponent->vertex_colors = colors;
}
if (!boneindices.empty())
{
meshcomponent->vertex_boneindices = boneindices;
}
if (!boneweights.empty())
{
meshcomponent->vertex_boneweights = boneweights;
}
meshcomponent->CreateRenderData();
}
//// DEBUG
//{
// const uint32_t width = objectcomponent->lightmapWidth;
// const uint32_t height = objectcomponent->lightmapHeight;
// objectcomponent->lightmapTextureData.resize(width * height * 4);
// const xatlas::OutputMesh *mesh = xatlas::GetOutputMeshes(atlas)[0];
// // Rasterize mesh triangles.
// const uint8_t white[] = { 255, 255, 255 };
// for (uint32_t j = 0; j < mesh->indexCount; j += 3) {
// int verts[3][2];
// uint8_t color[4];
// for (int k = 0; k < 3; k++) {
// const xatlas::OutputVertex &v = mesh->vertexArray[mesh->indexArray[j + k]];
// verts[k][0] = int(v.uv[0]);
// verts[k][1] = int(v.uv[1]);
// color[k] = rand() % 255;
// }
// color[3] = 255;
// if (!verts[0][0] && !verts[0][1] && !verts[1][0] && !verts[1][1] && !verts[2][0] && !verts[2][1])
// continue; // Skip triangles that weren't atlased.
// RasterizeTriangle(objectcomponent->lightmapTextureData.data(), width, verts[0], verts[1], verts[2], color);
// RasterizeLine(objectcomponent->lightmapTextureData.data(), width, verts[0], verts[1], white);
// RasterizeLine(objectcomponent->lightmapTextureData.data(), width, verts[1], verts[2], white);
// RasterizeLine(objectcomponent->lightmapTextureData.data(), width, verts[2], verts[0], white);
// }
//}
xatlas::Destroy(atlas);
objectcomponent->SetLightmapRenderRequest(true);
wiRenderer::InvalidateBVH();
}
}
});
objectWindow->AddWidget(generateLightmapButton);
stopLightmapGenButton = new wiButton("Stop Lightmap Gen");
stopLightmapGenButton->SetTooltip("Stop the lightmap rendering and save the lightmap.");
stopLightmapGenButton->SetPos(XMFLOAT2(x, y += 30));
stopLightmapGenButton->SetSize(XMFLOAT2(140, 30));
stopLightmapGenButton->OnClick([&](wiEventArgs args) {
Scene& scene = wiRenderer::GetScene();
ObjectComponent* objectcomponent = scene.objects.GetComponent(entity);
if (objectcomponent != nullptr)
{
objectcomponent->SetLightmapRenderRequest(false);
objectcomponent->SaveLightmap();
}
});
objectWindow->AddWidget(stopLightmapGenButton);
clearLightmapButton = new wiButton("Clear Lightmap");
clearLightmapButton->SetTooltip("Clear the lightmap from this object.");
clearLightmapButton->SetPos(XMFLOAT2(x, y += 30));
clearLightmapButton->SetSize(XMFLOAT2(140, 30));
clearLightmapButton->OnClick([&](wiEventArgs args) {
Scene& scene = wiRenderer::GetScene();
ObjectComponent* objectcomponent = scene.objects.GetComponent(entity);
if (objectcomponent != nullptr)
{
objectcomponent->ClearLightmap();
}
});
objectWindow->AddWidget(clearLightmapButton);
objectWindow->Translate(XMFLOAT3(1300, 100, 0));
objectWindow->SetVisible(false);
SetEntity(INVALID_ENTITY);
}
ObjectWindow::~ObjectWindow()
{
objectWindow->RemoveWidgets(true);
GUI->RemoveWidget(objectWindow);
SAFE_DELETE(objectWindow);
}
void ObjectWindow::SetEntity(Entity entity)
{
if (this->entity == entity)
return;
this->entity = entity;
Scene& scene = wiRenderer::GetScene();
const ObjectComponent* object = scene.objects.GetComponent(entity);
if (object != nullptr)
{
const NameComponent* name = scene.names.GetComponent(entity);
if (name != nullptr)
{
std::stringstream ss("");
ss << name->name << " (" << entity << ")";
nameLabel->SetText(ss.str());
}
else
{
std::stringstream ss("");
ss<< "(" << entity << ")";
nameLabel->SetText(ss.str());
}
renderableCheckBox->SetCheck(object->IsRenderable());
cascadeMaskSlider->SetValue((float)object->cascadeMask);
ditherSlider->SetValue(object->GetTransparency());
const RigidBodyPhysicsComponent* physicsComponent = scene.rigidbodies.GetComponent(entity);
rigidBodyCheckBox->SetCheck(physicsComponent != nullptr);
if (physicsComponent != nullptr)
{
kinematicCheckBox->SetCheck(physicsComponent->IsKinematic());
disabledeactivationCheckBox->SetCheck(physicsComponent->IsDisableDeactivation());
if (physicsComponent->shape == RigidBodyPhysicsComponent::CollisionShape::BOX)
{
collisionShapeComboBox->SetSelected(0);
}
else if (physicsComponent->shape == RigidBodyPhysicsComponent::CollisionShape::SPHERE)
{
collisionShapeComboBox->SetSelected(1);
}
else if (physicsComponent->shape == RigidBodyPhysicsComponent::CollisionShape::CAPSULE)
{
collisionShapeComboBox->SetSelected(2);
}
else if (physicsComponent->shape == RigidBodyPhysicsComponent::CollisionShape::CONVEX_HULL)
{
collisionShapeComboBox->SetSelected(3);
}
else if (physicsComponent->shape == RigidBodyPhysicsComponent::CollisionShape::TRIANGLE_MESH)
{
collisionShapeComboBox->SetSelected(4);
}
}
objectWindow->SetEnabled(true);
}
else
{
objectWindow->SetEnabled(false);
}
}