197 lines
8.3 KiB
C++
197 lines
8.3 KiB
C++
#include <Orange/Graphics/LightmapAtlas.h>
|
|
|
|
namespace orange
|
|
{
|
|
Result<LightmapAtlas> CreateLightmapAtlasForMesh(MeshData& mesh)
|
|
{
|
|
LightmapAtlas atlas{ xatlas::Create(), [](xatlas::Atlas *obj) { xatlas::Destroy(obj); } };
|
|
|
|
auto& verts = mesh.vertexData.GetStaticVertices();
|
|
|
|
xatlas::MeshDecl meshDecl;
|
|
meshDecl.vertexCount = verts.size();
|
|
meshDecl.vertexPositionData = &verts[0].pos;
|
|
meshDecl.vertexPositionStride = sizeof(StaticVertex);
|
|
meshDecl.indexCount = (uint32_t)mesh.indices.size();
|
|
meshDecl.indexData = &mesh.indices[0];
|
|
meshDecl.indexFormat = sizeof(IndexType) == sizeof(uint32_t) ? xatlas::IndexFormat::UInt32 : xatlas::IndexFormat::UInt16;
|
|
|
|
xatlas::AddMeshError error = xatlas::AddMesh(atlas.get(), meshDecl, 0);
|
|
if (error != xatlas::AddMeshError::Success) {
|
|
log::info("Error adding mesh: %s", xatlas::StringForEnum(error));
|
|
return Result<LightmapAtlas>::Error(BasicErrorCode::Failed);
|
|
}
|
|
xatlas::AddMeshJoin(atlas.get());
|
|
|
|
log::info("Generating atlas...");
|
|
xatlas::ChartOptions chartOptions;
|
|
xatlas::PackOptions packOptions;
|
|
//packOptions.resolution = 8192;
|
|
xatlas::Generate(atlas.get(), chartOptions, packOptions);
|
|
|
|
log::info(" %d charts", atlas->chartCount);
|
|
log::info(" %d atlases", atlas->atlasCount);
|
|
for (uint32_t i = 0; i < atlas->atlasCount; i++)
|
|
log::info(" %d: %0.2f%% utilization", i, atlas->utilization[i] * 100.0f);
|
|
log::info(" %ux%u resolution", atlas->width, atlas->height);
|
|
|
|
if (atlas->width > 0 && atlas->height > 0)
|
|
{
|
|
log::info("Updating mesh with new light UVs.");
|
|
|
|
for (uint32_t i = 0; i < atlas->meshCount; i++)
|
|
{
|
|
// TODO: Multi meshes
|
|
const xatlas::Mesh &xatlas_mesh = atlas->meshes[i];
|
|
|
|
Vector<StaticVertex> atlasedVerts;
|
|
atlasedVerts.resize(xatlas_mesh.vertexCount);
|
|
|
|
// Update vertices with new set.
|
|
for (uint32_t j = 0; j < xatlas_mesh.vertexCount; j++)
|
|
{
|
|
const xatlas::Vertex &v = xatlas_mesh.vertexArray[j];
|
|
|
|
atlasedVerts[j].pos = verts[v.xref].pos;
|
|
atlasedVerts[j].texUV = verts[v.xref].texUV;
|
|
atlasedVerts[j].lightUV = vec2(v.uv[0] / (float)atlas->width, v.uv[1] / (float)atlas->height);
|
|
atlasedVerts[j].normal = verts[v.xref].normal;
|
|
}
|
|
|
|
verts.resize(atlasedVerts.size());
|
|
atlasedVerts.copy(verts.data());
|
|
|
|
// Update indices
|
|
for (uint32_t j = 0; j < xatlas_mesh.indexCount; j++)
|
|
{
|
|
mesh.indices[j] = xatlas_mesh.indexArray[j];
|
|
}
|
|
}
|
|
}
|
|
|
|
return Result<LightmapAtlas>::Success(std::move(atlas));
|
|
}
|
|
|
|
// This code is just stolen from the XAtlas sample.
|
|
static void RandomColor(uint8_t *color)
|
|
{
|
|
for (int i = 0; i < 3; i++)
|
|
color[i] = uint8_t((rand() % 255 + 192) * 0.5f);
|
|
color[3] = 255;
|
|
}
|
|
|
|
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] = 255;
|
|
}
|
|
|
|
// https://github.com/miloyip/line/blob/master/line_bresenham.c
|
|
// License: public domain.
|
|
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
|
|
Copyright Dmitry V. Sokolov
|
|
|
|
This software is provided 'as-is', without any express or implied warranty.
|
|
In no event will the authors be held liable for any damages arising from the use of this software.
|
|
Permission is granted to anyone to use this software for any purpose,
|
|
including commercial applications, and to alter it and redistribute it freely,
|
|
subject to the following restrictions:
|
|
|
|
1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
|
|
2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
|
3. This notice may not be removed or altered from any source distribution.
|
|
*/
|
|
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);
|
|
}
|
|
}
|
|
|
|
Vector<uint8_t> GenerateDebugAtlasTexture(const LightmapAtlas& lightmapAtlas)
|
|
{
|
|
const xatlas::Atlas *atlas = lightmapAtlas.get();
|
|
Vector<uint8_t> outputChartsImage;
|
|
|
|
if (atlas->width > 0 && atlas->height > 0)
|
|
{
|
|
printf("Rasterizing result...\n");
|
|
|
|
// Dump images.
|
|
const uint32_t imageDataSize = atlas->width * atlas->height * 4;
|
|
outputChartsImage.resize(atlas->atlasCount * imageDataSize);
|
|
for (uint32_t i = 0; i < atlas->meshCount; i++)
|
|
{
|
|
const xatlas::Mesh &mesh = atlas->meshes[i];
|
|
|
|
const uint8_t white[] = { 255, 255, 255, 255 };
|
|
|
|
// Rasterize mesh charts.
|
|
for (uint32_t j = 0; j < mesh.chartCount; j++)
|
|
{
|
|
const xatlas::Chart *chart = &mesh.chartArray[j];
|
|
uint8_t color[4];
|
|
RandomColor(color);
|
|
for (uint32_t k = 0; k < chart->faceCount; k++)
|
|
{
|
|
const uint32_t face = chart->faceArray[k];
|
|
|
|
const uint32_t faceVertexCount = 3;
|
|
const uint32_t faceFirstIndex = face * 3;
|
|
|
|
int verts[255][2];
|
|
for (uint32_t l = 0; l < faceVertexCount; l++)
|
|
{
|
|
const xatlas::Vertex &v = mesh.vertexArray[mesh.indexArray[faceFirstIndex + l]];
|
|
verts[l][0] = int(v.uv[0]);
|
|
verts[l][1] = int(v.uv[1]);
|
|
}
|
|
uint8_t *imageData = &outputChartsImage[chart->atlasIndex * imageDataSize];
|
|
|
|
RasterizeTriangle(imageData, atlas->width, verts[0], verts[1], verts[2], color);
|
|
|
|
for (uint32_t l = 0; l < faceVertexCount; l++)
|
|
RasterizeLine(imageData, atlas->width, verts[l], verts[(l + 1) % faceVertexCount], white);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return outputChartsImage;
|
|
}
|
|
} |