Add UV map packer
parent
2b83283961
commit
5fc51c3b02
|
@ -318,6 +318,8 @@ HEADERS += ../dust3d/uv/triangulate.h
|
|||
SOURCES += ../dust3d/uv/triangulate.cc
|
||||
HEADERS += ../dust3d/uv/unwrap_uv.h
|
||||
SOURCES += ../dust3d/uv/unwrap_uv.cc
|
||||
HEADERS += ../dust3d/uv/uv_map_packer.h
|
||||
SOURCES += ../dust3d/uv/uv_map_packer.cc
|
||||
HEADERS += ../dust3d/uv/uv_mesh_data_type.h
|
||||
SOURCES += ../dust3d/uv/uv_mesh_data_type.cc
|
||||
HEADERS += ../dust3d/uv/uv_unwrapper.h
|
||||
|
|
|
@ -23,13 +23,16 @@
|
|||
#ifndef DUST3D_BASE_OBJECT_H_
|
||||
#define DUST3D_BASE_OBJECT_H_
|
||||
|
||||
#include <array>
|
||||
#include <dust3d/base/color.h>
|
||||
#include <dust3d/base/position_key.h>
|
||||
#include <dust3d/base/rectangle.h>
|
||||
#include <dust3d/base/uuid.h>
|
||||
#include <dust3d/base/vector2.h>
|
||||
#include <dust3d/base/vector3.h>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace dust3d {
|
||||
|
@ -59,6 +62,7 @@ public:
|
|||
std::vector<std::pair<Uuid, Uuid>> vertexSourceNodes;
|
||||
std::vector<std::vector<size_t>> triangleAndQuads;
|
||||
std::vector<std::vector<size_t>> triangles;
|
||||
std::unordered_map<Uuid, std::map<std::array<PositionKey, 3>, std::array<Vector2, 3>>> partTriangleUvs;
|
||||
std::vector<Vector3> triangleNormals;
|
||||
std::vector<Color> triangleColors;
|
||||
bool alphaEnabled = false;
|
||||
|
|
|
@ -849,6 +849,7 @@ std::unique_ptr<MeshCombiner::Mesh> MeshGenerator::combineComponentMesh(const st
|
|||
for (const auto& vertex : partCache.vertices)
|
||||
componentCache.noneSeamVertices.insert(vertex);
|
||||
collectSharedQuadEdges(partCache.vertices, partCache.faces, &componentCache.sharedQuadEdges);
|
||||
componentCache.partTriangleUvs.insert({ Uuid(partIdString), partCache.triangleUvs });
|
||||
for (const auto& it : partCache.objectNodes)
|
||||
componentCache.objectNodes.push_back(it);
|
||||
for (const auto& it : partCache.objectEdges)
|
||||
|
@ -989,6 +990,8 @@ std::unique_ptr<MeshCombiner::Mesh> MeshGenerator::combineComponentChildGroupMes
|
|||
componentCache.noneSeamVertices.insert(vertex);
|
||||
for (const auto& it : childComponentCache.sharedQuadEdges)
|
||||
componentCache.sharedQuadEdges.insert(it);
|
||||
for (const auto& it : childComponentCache.partTriangleUvs)
|
||||
componentCache.partTriangleUvs.insert({ it.first, it.second });
|
||||
for (const auto& it : childComponentCache.objectNodes)
|
||||
componentCache.objectNodes.push_back(it);
|
||||
for (const auto& it : childComponentCache.objectEdges)
|
||||
|
@ -1356,6 +1359,7 @@ void MeshGenerator::generate()
|
|||
|
||||
m_object->nodes = componentCache.objectNodes;
|
||||
m_object->edges = componentCache.objectEdges;
|
||||
m_object->partTriangleUvs = componentCache.partTriangleUvs;
|
||||
m_nodeVertices = componentCache.objectNodeVertices;
|
||||
|
||||
std::vector<Vector3> combinedVertices;
|
||||
|
|
|
@ -58,6 +58,7 @@ public:
|
|||
struct GeneratedComponent {
|
||||
std::unique_ptr<MeshCombiner::Mesh> mesh;
|
||||
std::set<std::pair<PositionKey, PositionKey>> sharedQuadEdges;
|
||||
std::unordered_map<Uuid, std::map<std::array<PositionKey, 3>, std::array<Vector2, 3>>> partTriangleUvs;
|
||||
std::set<PositionKey> noneSeamVertices;
|
||||
std::vector<ObjectNode> objectNodes;
|
||||
std::vector<std::pair<std::pair<Uuid, Uuid>, std::pair<Uuid, Uuid>>> objectEdges;
|
||||
|
|
|
@ -25,92 +25,90 @@
|
|||
#include <dust3d/uv/max_rectangles.h>
|
||||
|
||||
namespace dust3d {
|
||||
namespace uv {
|
||||
|
||||
void ChartPacker::setCharts(const std::vector<std::pair<float, float>>& chartSizes)
|
||||
{
|
||||
m_chartSizes = chartSizes;
|
||||
}
|
||||
void ChartPacker::setCharts(const std::vector<std::pair<float, float>>& chartSizes)
|
||||
{
|
||||
m_chartSizes = chartSizes;
|
||||
}
|
||||
|
||||
const std::vector<std::tuple<float, float, float, float, bool>>& ChartPacker::getResult()
|
||||
{
|
||||
return m_result;
|
||||
}
|
||||
const std::vector<std::tuple<float, float, float, float, bool>>& ChartPacker::getResult()
|
||||
{
|
||||
return m_result;
|
||||
}
|
||||
|
||||
double ChartPacker::calculateTotalArea()
|
||||
{
|
||||
double totalArea = 0;
|
||||
for (const auto& chartSize : m_chartSizes) {
|
||||
totalArea += chartSize.first * chartSize.second;
|
||||
}
|
||||
return totalArea;
|
||||
double ChartPacker::calculateTotalArea()
|
||||
{
|
||||
double totalArea = 0;
|
||||
for (const auto& chartSize : m_chartSizes) {
|
||||
totalArea += chartSize.first * chartSize.second;
|
||||
}
|
||||
return totalArea;
|
||||
}
|
||||
|
||||
bool ChartPacker::tryPack(float textureSize)
|
||||
{
|
||||
std::vector<MaxRectanglesSize> rects;
|
||||
int width = textureSize * m_floatToIntFactor;
|
||||
int height = width;
|
||||
float paddingSize = m_paddingSize * width;
|
||||
float paddingSize2 = paddingSize + paddingSize;
|
||||
for (const auto& chartSize : m_chartSizes) {
|
||||
MaxRectanglesSize r;
|
||||
r.width = chartSize.first * m_floatToIntFactor + paddingSize2;
|
||||
r.height = chartSize.second * m_floatToIntFactor + paddingSize2;
|
||||
rects.push_back(r);
|
||||
}
|
||||
const MaxRectanglesFreeRectChoiceHeuristic methods[] = {
|
||||
kMaxRectanglesBestShortSideFit,
|
||||
kMaxRectanglesBestLongSideFit,
|
||||
kMaxRectanglesBestAreaFit,
|
||||
kMaxRectanglesBottomLeftRule,
|
||||
kMaxRectanglesContactPointRule
|
||||
};
|
||||
float occupancy = 0;
|
||||
float bestOccupancy = 0;
|
||||
std::vector<MaxRectanglesPosition> bestResult;
|
||||
for (size_t i = 0; i < sizeof(methods) / sizeof(methods[0]); ++i) {
|
||||
std::vector<MaxRectanglesPosition> result(rects.size());
|
||||
if (0 != maxRectangles(width, height, rects.size(), rects.data(), methods[i], true, result.data(), &occupancy)) {
|
||||
continue;
|
||||
}
|
||||
if (occupancy > bestOccupancy) {
|
||||
bestResult = result;
|
||||
bestOccupancy = occupancy;
|
||||
}
|
||||
}
|
||||
if (bestResult.size() != rects.size())
|
||||
return false;
|
||||
m_result.resize(bestResult.size());
|
||||
for (decltype(bestResult.size()) i = 0; i < bestResult.size(); ++i) {
|
||||
const auto& result = bestResult[i];
|
||||
const auto& rect = rects[i];
|
||||
auto& dest = m_result[i];
|
||||
std::get<0>(dest) = (float)(result.left + paddingSize) / width;
|
||||
std::get<1>(dest) = (float)(result.top + paddingSize) / height;
|
||||
std::get<2>(dest) = (float)(rect.width - paddingSize2) / width;
|
||||
std::get<3>(dest) = (float)(rect.height - paddingSize2) / height;
|
||||
std::get<4>(dest) = result.rotated;
|
||||
}
|
||||
return true;
|
||||
bool ChartPacker::tryPack(float textureSize)
|
||||
{
|
||||
std::vector<uv::MaxRectanglesSize> rects;
|
||||
int width = textureSize * m_floatToIntFactor;
|
||||
int height = width;
|
||||
float paddingSize = m_paddingSize * width;
|
||||
float paddingSize2 = paddingSize + paddingSize;
|
||||
for (const auto& chartSize : m_chartSizes) {
|
||||
uv::MaxRectanglesSize r;
|
||||
r.width = chartSize.first * m_floatToIntFactor + paddingSize2;
|
||||
r.height = chartSize.second * m_floatToIntFactor + paddingSize2;
|
||||
rects.push_back(r);
|
||||
}
|
||||
const uv::MaxRectanglesFreeRectChoiceHeuristic methods[] = {
|
||||
uv::kMaxRectanglesBestShortSideFit,
|
||||
uv::kMaxRectanglesBestLongSideFit,
|
||||
uv::kMaxRectanglesBestAreaFit,
|
||||
uv::kMaxRectanglesBottomLeftRule,
|
||||
uv::kMaxRectanglesContactPointRule
|
||||
};
|
||||
float occupancy = 0;
|
||||
float bestOccupancy = 0;
|
||||
std::vector<uv::MaxRectanglesPosition> bestResult;
|
||||
for (size_t i = 0; i < sizeof(methods) / sizeof(methods[0]); ++i) {
|
||||
std::vector<uv::MaxRectanglesPosition> result(rects.size());
|
||||
if (0 != maxRectangles(width, height, rects.size(), rects.data(), methods[i], true, result.data(), &occupancy)) {
|
||||
continue;
|
||||
}
|
||||
if (occupancy > bestOccupancy) {
|
||||
bestResult = result;
|
||||
bestOccupancy = occupancy;
|
||||
}
|
||||
}
|
||||
if (bestResult.size() != rects.size())
|
||||
return false;
|
||||
m_result.resize(bestResult.size());
|
||||
for (decltype(bestResult.size()) i = 0; i < bestResult.size(); ++i) {
|
||||
const auto& result = bestResult[i];
|
||||
const auto& rect = rects[i];
|
||||
auto& dest = m_result[i];
|
||||
std::get<0>(dest) = (float)(result.left + paddingSize) / width;
|
||||
std::get<1>(dest) = (float)(result.top + paddingSize) / height;
|
||||
std::get<2>(dest) = (float)(rect.width - paddingSize2) / width;
|
||||
std::get<3>(dest) = (float)(rect.height - paddingSize2) / height;
|
||||
std::get<4>(dest) = result.rotated;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
float ChartPacker::pack()
|
||||
{
|
||||
float textureSize = 0;
|
||||
float initialGuessSize = std::sqrt(calculateTotalArea() * m_initialAreaGuessFactor);
|
||||
while (true) {
|
||||
textureSize = initialGuessSize * m_textureSizeFactor;
|
||||
++m_tryNum;
|
||||
if (tryPack(textureSize))
|
||||
break;
|
||||
m_textureSizeFactor += m_textureSizeGrowFactor;
|
||||
if (m_tryNum >= m_maxTryNum) {
|
||||
break;
|
||||
}
|
||||
float ChartPacker::pack()
|
||||
{
|
||||
float textureSize = 0;
|
||||
float initialGuessSize = std::sqrt(calculateTotalArea() * m_initialAreaGuessFactor);
|
||||
while (true) {
|
||||
textureSize = initialGuessSize * m_textureSizeFactor;
|
||||
++m_tryNum;
|
||||
if (tryPack(textureSize))
|
||||
break;
|
||||
m_textureSizeFactor += m_textureSizeGrowFactor;
|
||||
if (m_tryNum >= m_maxTryNum) {
|
||||
break;
|
||||
}
|
||||
return textureSize;
|
||||
}
|
||||
return textureSize;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,30 +28,28 @@
|
|||
#include <vector>
|
||||
|
||||
namespace dust3d {
|
||||
namespace uv {
|
||||
|
||||
class ChartPacker {
|
||||
public:
|
||||
void setCharts(const std::vector<std::pair<float, float>>& chartSizes);
|
||||
const std::vector<std::tuple<float, float, float, float, bool>>& getResult();
|
||||
float pack();
|
||||
bool tryPack(float textureSize);
|
||||
class ChartPacker {
|
||||
public:
|
||||
void setCharts(const std::vector<std::pair<float, float>>& chartSizes);
|
||||
const std::vector<std::tuple<float, float, float, float, bool>>& getResult();
|
||||
float pack();
|
||||
bool tryPack(float textureSize);
|
||||
|
||||
private:
|
||||
double calculateTotalArea();
|
||||
private:
|
||||
double calculateTotalArea();
|
||||
|
||||
std::vector<std::pair<float, float>> m_chartSizes;
|
||||
std::vector<std::tuple<float, float, float, float, bool>> m_result;
|
||||
float m_initialAreaGuessFactor = 1.1;
|
||||
float m_textureSizeGrowFactor = 0.05;
|
||||
float m_floatToIntFactor = 10000;
|
||||
size_t m_tryNum = 0;
|
||||
float m_textureSizeFactor = 1.0;
|
||||
float m_paddingSize = 0.005;
|
||||
size_t m_maxTryNum = 100;
|
||||
};
|
||||
std::vector<std::pair<float, float>> m_chartSizes;
|
||||
std::vector<std::tuple<float, float, float, float, bool>> m_result;
|
||||
float m_initialAreaGuessFactor = 1.1;
|
||||
float m_textureSizeGrowFactor = 0.05;
|
||||
float m_floatToIntFactor = 10000;
|
||||
size_t m_tryNum = 0;
|
||||
float m_textureSizeFactor = 1.0;
|
||||
float m_paddingSize = 0.005;
|
||||
size_t m_maxTryNum = 100;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* Copyright (c) 2016-2022 Jeremy HU <jeremy-at-dust3d dot org>. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <dust3d/uv/chart_packer.h>
|
||||
#include <dust3d/uv/uv_map_packer.h>
|
||||
|
||||
namespace dust3d {
|
||||
|
||||
UvMapPacker::UvMapPacker()
|
||||
{
|
||||
}
|
||||
|
||||
void UvMapPacker::addPart(const Part& part)
|
||||
{
|
||||
m_partTriangleUvs.push_back(part);
|
||||
}
|
||||
|
||||
void UvMapPacker::pack()
|
||||
{
|
||||
if (m_partTriangleUvs.empty())
|
||||
return;
|
||||
|
||||
std::vector<std::pair<float, float>> chartSizes(m_partTriangleUvs.size());
|
||||
for (size_t i = 0; i < m_partTriangleUvs.size(); ++i) {
|
||||
const auto& part = m_partTriangleUvs[i];
|
||||
chartSizes[i] = { part.width, part.height };
|
||||
}
|
||||
|
||||
ChartPacker chartPacker;
|
||||
chartPacker.setCharts(chartSizes);
|
||||
m_packedTextureSize = chartPacker.pack();
|
||||
const auto& packedResult = chartPacker.getResult();
|
||||
for (size_t i = 0; i < packedResult.size(); ++i) {
|
||||
auto& part = m_partTriangleUvs[i];
|
||||
const auto& result = packedResult[i];
|
||||
auto& left = std::get<0>(result);
|
||||
auto& top = std::get<1>(result);
|
||||
auto& width = std::get<2>(result);
|
||||
auto& height = std::get<3>(result);
|
||||
auto& flipped = std::get<4>(result);
|
||||
Layout layout;
|
||||
layout.id = part.id;
|
||||
layout.flipped = flipped;
|
||||
if (flipped) {
|
||||
layout.left = left;
|
||||
layout.top = top;
|
||||
layout.width = height;
|
||||
layout.height = width;
|
||||
} else {
|
||||
layout.left = left;
|
||||
layout.top = top;
|
||||
layout.width = width;
|
||||
layout.height = height;
|
||||
}
|
||||
if (flipped) {
|
||||
for (auto& it : part.localUv) {
|
||||
std::swap(it.second[0][0], it.second[0][1]);
|
||||
std::swap(it.second[1][0], it.second[1][1]);
|
||||
std::swap(it.second[2][0], it.second[2][1]);
|
||||
}
|
||||
}
|
||||
for (const auto& it : part.localUv) {
|
||||
layout.globalUv.insert({ it.first,
|
||||
std::array<Vector2, 3> {
|
||||
Vector2((left + it.second[0].x() * width) / m_packedTextureSize,
|
||||
(top + it.second[0].y() * height) / m_packedTextureSize),
|
||||
Vector2((left + it.second[1].x() * width) / m_packedTextureSize,
|
||||
(top + it.second[1].y() * height) / m_packedTextureSize),
|
||||
Vector2((left + it.second[2].x() * width) / m_packedTextureSize,
|
||||
(top + it.second[2].y() * height) / m_packedTextureSize) } });
|
||||
}
|
||||
m_packedLayouts.emplace_back(layout);
|
||||
}
|
||||
}
|
||||
|
||||
double UvMapPacker::packedTextureSize()
|
||||
{
|
||||
return m_packedTextureSize;
|
||||
}
|
||||
|
||||
const std::vector<UvMapPacker::Layout>& UvMapPacker::packedLayouts()
|
||||
{
|
||||
return m_packedLayouts;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Copyright (c) 2016-2022 Jeremy HU <jeremy-at-dust3d dot org>. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef DUST3D_UV_MAP_PACKER_H_
|
||||
#define DUST3D_UV_MAP_PACKER_H_
|
||||
|
||||
#include <array>
|
||||
#include <dust3d/base/position_key.h>
|
||||
#include <dust3d/base/uuid.h>
|
||||
#include <dust3d/base/vector2.h>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
namespace dust3d {
|
||||
|
||||
class UvMapPacker {
|
||||
public:
|
||||
struct Part {
|
||||
Uuid id;
|
||||
double width;
|
||||
double height;
|
||||
std::map<std::array<PositionKey, 3>, std::array<Vector2, 3>> localUv;
|
||||
};
|
||||
|
||||
struct Layout {
|
||||
Uuid id;
|
||||
double left;
|
||||
double top;
|
||||
double width;
|
||||
double height;
|
||||
bool flipped;
|
||||
std::map<std::array<PositionKey, 3>, std::array<Vector2, 3>> globalUv;
|
||||
};
|
||||
|
||||
UvMapPacker();
|
||||
void addPart(const Part& part);
|
||||
void pack();
|
||||
const std::vector<Layout>& packedLayouts();
|
||||
double packedTextureSize();
|
||||
|
||||
private:
|
||||
std::vector<Part> m_partTriangleUvs;
|
||||
std::vector<Layout> m_packedLayouts;
|
||||
double m_packedTextureSize = 0.0;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue