Add UV map packer
parent
2b83283961
commit
5fc51c3b02
|
@ -318,6 +318,8 @@ HEADERS += ../dust3d/uv/triangulate.h
|
||||||
SOURCES += ../dust3d/uv/triangulate.cc
|
SOURCES += ../dust3d/uv/triangulate.cc
|
||||||
HEADERS += ../dust3d/uv/unwrap_uv.h
|
HEADERS += ../dust3d/uv/unwrap_uv.h
|
||||||
SOURCES += ../dust3d/uv/unwrap_uv.cc
|
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
|
HEADERS += ../dust3d/uv/uv_mesh_data_type.h
|
||||||
SOURCES += ../dust3d/uv/uv_mesh_data_type.cc
|
SOURCES += ../dust3d/uv/uv_mesh_data_type.cc
|
||||||
HEADERS += ../dust3d/uv/uv_unwrapper.h
|
HEADERS += ../dust3d/uv/uv_unwrapper.h
|
||||||
|
|
|
@ -23,13 +23,16 @@
|
||||||
#ifndef DUST3D_BASE_OBJECT_H_
|
#ifndef DUST3D_BASE_OBJECT_H_
|
||||||
#define DUST3D_BASE_OBJECT_H_
|
#define DUST3D_BASE_OBJECT_H_
|
||||||
|
|
||||||
|
#include <array>
|
||||||
#include <dust3d/base/color.h>
|
#include <dust3d/base/color.h>
|
||||||
|
#include <dust3d/base/position_key.h>
|
||||||
#include <dust3d/base/rectangle.h>
|
#include <dust3d/base/rectangle.h>
|
||||||
#include <dust3d/base/uuid.h>
|
#include <dust3d/base/uuid.h>
|
||||||
#include <dust3d/base/vector2.h>
|
#include <dust3d/base/vector2.h>
|
||||||
#include <dust3d/base/vector3.h>
|
#include <dust3d/base/vector3.h>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace dust3d {
|
namespace dust3d {
|
||||||
|
@ -59,6 +62,7 @@ public:
|
||||||
std::vector<std::pair<Uuid, Uuid>> vertexSourceNodes;
|
std::vector<std::pair<Uuid, Uuid>> vertexSourceNodes;
|
||||||
std::vector<std::vector<size_t>> triangleAndQuads;
|
std::vector<std::vector<size_t>> triangleAndQuads;
|
||||||
std::vector<std::vector<size_t>> triangles;
|
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<Vector3> triangleNormals;
|
||||||
std::vector<Color> triangleColors;
|
std::vector<Color> triangleColors;
|
||||||
bool alphaEnabled = false;
|
bool alphaEnabled = false;
|
||||||
|
|
|
@ -849,6 +849,7 @@ std::unique_ptr<MeshCombiner::Mesh> MeshGenerator::combineComponentMesh(const st
|
||||||
for (const auto& vertex : partCache.vertices)
|
for (const auto& vertex : partCache.vertices)
|
||||||
componentCache.noneSeamVertices.insert(vertex);
|
componentCache.noneSeamVertices.insert(vertex);
|
||||||
collectSharedQuadEdges(partCache.vertices, partCache.faces, &componentCache.sharedQuadEdges);
|
collectSharedQuadEdges(partCache.vertices, partCache.faces, &componentCache.sharedQuadEdges);
|
||||||
|
componentCache.partTriangleUvs.insert({ Uuid(partIdString), partCache.triangleUvs });
|
||||||
for (const auto& it : partCache.objectNodes)
|
for (const auto& it : partCache.objectNodes)
|
||||||
componentCache.objectNodes.push_back(it);
|
componentCache.objectNodes.push_back(it);
|
||||||
for (const auto& it : partCache.objectEdges)
|
for (const auto& it : partCache.objectEdges)
|
||||||
|
@ -989,6 +990,8 @@ std::unique_ptr<MeshCombiner::Mesh> MeshGenerator::combineComponentChildGroupMes
|
||||||
componentCache.noneSeamVertices.insert(vertex);
|
componentCache.noneSeamVertices.insert(vertex);
|
||||||
for (const auto& it : childComponentCache.sharedQuadEdges)
|
for (const auto& it : childComponentCache.sharedQuadEdges)
|
||||||
componentCache.sharedQuadEdges.insert(it);
|
componentCache.sharedQuadEdges.insert(it);
|
||||||
|
for (const auto& it : childComponentCache.partTriangleUvs)
|
||||||
|
componentCache.partTriangleUvs.insert({ it.first, it.second });
|
||||||
for (const auto& it : childComponentCache.objectNodes)
|
for (const auto& it : childComponentCache.objectNodes)
|
||||||
componentCache.objectNodes.push_back(it);
|
componentCache.objectNodes.push_back(it);
|
||||||
for (const auto& it : childComponentCache.objectEdges)
|
for (const auto& it : childComponentCache.objectEdges)
|
||||||
|
@ -1356,6 +1359,7 @@ void MeshGenerator::generate()
|
||||||
|
|
||||||
m_object->nodes = componentCache.objectNodes;
|
m_object->nodes = componentCache.objectNodes;
|
||||||
m_object->edges = componentCache.objectEdges;
|
m_object->edges = componentCache.objectEdges;
|
||||||
|
m_object->partTriangleUvs = componentCache.partTriangleUvs;
|
||||||
m_nodeVertices = componentCache.objectNodeVertices;
|
m_nodeVertices = componentCache.objectNodeVertices;
|
||||||
|
|
||||||
std::vector<Vector3> combinedVertices;
|
std::vector<Vector3> combinedVertices;
|
||||||
|
|
|
@ -58,6 +58,7 @@ public:
|
||||||
struct GeneratedComponent {
|
struct GeneratedComponent {
|
||||||
std::unique_ptr<MeshCombiner::Mesh> mesh;
|
std::unique_ptr<MeshCombiner::Mesh> mesh;
|
||||||
std::set<std::pair<PositionKey, PositionKey>> sharedQuadEdges;
|
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::set<PositionKey> noneSeamVertices;
|
||||||
std::vector<ObjectNode> objectNodes;
|
std::vector<ObjectNode> objectNodes;
|
||||||
std::vector<std::pair<std::pair<Uuid, Uuid>, std::pair<Uuid, Uuid>>> objectEdges;
|
std::vector<std::pair<std::pair<Uuid, Uuid>, std::pair<Uuid, Uuid>>> objectEdges;
|
||||||
|
|
|
@ -25,52 +25,51 @@
|
||||||
#include <dust3d/uv/max_rectangles.h>
|
#include <dust3d/uv/max_rectangles.h>
|
||||||
|
|
||||||
namespace dust3d {
|
namespace dust3d {
|
||||||
namespace uv {
|
|
||||||
|
|
||||||
void ChartPacker::setCharts(const std::vector<std::pair<float, float>>& chartSizes)
|
void ChartPacker::setCharts(const std::vector<std::pair<float, float>>& chartSizes)
|
||||||
{
|
{
|
||||||
m_chartSizes = chartSizes;
|
m_chartSizes = chartSizes;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::vector<std::tuple<float, float, float, float, bool>>& ChartPacker::getResult()
|
const std::vector<std::tuple<float, float, float, float, bool>>& ChartPacker::getResult()
|
||||||
{
|
{
|
||||||
return m_result;
|
return m_result;
|
||||||
}
|
}
|
||||||
|
|
||||||
double ChartPacker::calculateTotalArea()
|
double ChartPacker::calculateTotalArea()
|
||||||
{
|
{
|
||||||
double totalArea = 0;
|
double totalArea = 0;
|
||||||
for (const auto& chartSize : m_chartSizes) {
|
for (const auto& chartSize : m_chartSizes) {
|
||||||
totalArea += chartSize.first * chartSize.second;
|
totalArea += chartSize.first * chartSize.second;
|
||||||
}
|
}
|
||||||
return totalArea;
|
return totalArea;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ChartPacker::tryPack(float textureSize)
|
bool ChartPacker::tryPack(float textureSize)
|
||||||
{
|
{
|
||||||
std::vector<MaxRectanglesSize> rects;
|
std::vector<uv::MaxRectanglesSize> rects;
|
||||||
int width = textureSize * m_floatToIntFactor;
|
int width = textureSize * m_floatToIntFactor;
|
||||||
int height = width;
|
int height = width;
|
||||||
float paddingSize = m_paddingSize * width;
|
float paddingSize = m_paddingSize * width;
|
||||||
float paddingSize2 = paddingSize + paddingSize;
|
float paddingSize2 = paddingSize + paddingSize;
|
||||||
for (const auto& chartSize : m_chartSizes) {
|
for (const auto& chartSize : m_chartSizes) {
|
||||||
MaxRectanglesSize r;
|
uv::MaxRectanglesSize r;
|
||||||
r.width = chartSize.first * m_floatToIntFactor + paddingSize2;
|
r.width = chartSize.first * m_floatToIntFactor + paddingSize2;
|
||||||
r.height = chartSize.second * m_floatToIntFactor + paddingSize2;
|
r.height = chartSize.second * m_floatToIntFactor + paddingSize2;
|
||||||
rects.push_back(r);
|
rects.push_back(r);
|
||||||
}
|
}
|
||||||
const MaxRectanglesFreeRectChoiceHeuristic methods[] = {
|
const uv::MaxRectanglesFreeRectChoiceHeuristic methods[] = {
|
||||||
kMaxRectanglesBestShortSideFit,
|
uv::kMaxRectanglesBestShortSideFit,
|
||||||
kMaxRectanglesBestLongSideFit,
|
uv::kMaxRectanglesBestLongSideFit,
|
||||||
kMaxRectanglesBestAreaFit,
|
uv::kMaxRectanglesBestAreaFit,
|
||||||
kMaxRectanglesBottomLeftRule,
|
uv::kMaxRectanglesBottomLeftRule,
|
||||||
kMaxRectanglesContactPointRule
|
uv::kMaxRectanglesContactPointRule
|
||||||
};
|
};
|
||||||
float occupancy = 0;
|
float occupancy = 0;
|
||||||
float bestOccupancy = 0;
|
float bestOccupancy = 0;
|
||||||
std::vector<MaxRectanglesPosition> bestResult;
|
std::vector<uv::MaxRectanglesPosition> bestResult;
|
||||||
for (size_t i = 0; i < sizeof(methods) / sizeof(methods[0]); ++i) {
|
for (size_t i = 0; i < sizeof(methods) / sizeof(methods[0]); ++i) {
|
||||||
std::vector<MaxRectanglesPosition> result(rects.size());
|
std::vector<uv::MaxRectanglesPosition> result(rects.size());
|
||||||
if (0 != maxRectangles(width, height, rects.size(), rects.data(), methods[i], true, result.data(), &occupancy)) {
|
if (0 != maxRectangles(width, height, rects.size(), rects.data(), methods[i], true, result.data(), &occupancy)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -93,10 +92,10 @@ namespace uv {
|
||||||
std::get<4>(dest) = result.rotated;
|
std::get<4>(dest) = result.rotated;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
float ChartPacker::pack()
|
float ChartPacker::pack()
|
||||||
{
|
{
|
||||||
float textureSize = 0;
|
float textureSize = 0;
|
||||||
float initialGuessSize = std::sqrt(calculateTotalArea() * m_initialAreaGuessFactor);
|
float initialGuessSize = std::sqrt(calculateTotalArea() * m_initialAreaGuessFactor);
|
||||||
while (true) {
|
while (true) {
|
||||||
|
@ -110,7 +109,6 @@ namespace uv {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return textureSize;
|
return textureSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
|
@ -28,16 +28,15 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace dust3d {
|
namespace dust3d {
|
||||||
namespace uv {
|
|
||||||
|
|
||||||
class ChartPacker {
|
class ChartPacker {
|
||||||
public:
|
public:
|
||||||
void setCharts(const std::vector<std::pair<float, float>>& chartSizes);
|
void setCharts(const std::vector<std::pair<float, float>>& chartSizes);
|
||||||
const std::vector<std::tuple<float, float, float, float, bool>>& getResult();
|
const std::vector<std::tuple<float, float, float, float, bool>>& getResult();
|
||||||
float pack();
|
float pack();
|
||||||
bool tryPack(float textureSize);
|
bool tryPack(float textureSize);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
double calculateTotalArea();
|
double calculateTotalArea();
|
||||||
|
|
||||||
std::vector<std::pair<float, float>> m_chartSizes;
|
std::vector<std::pair<float, float>> m_chartSizes;
|
||||||
|
@ -49,9 +48,8 @@ namespace uv {
|
||||||
float m_textureSizeFactor = 1.0;
|
float m_textureSizeFactor = 1.0;
|
||||||
float m_paddingSize = 0.005;
|
float m_paddingSize = 0.005;
|
||||||
size_t m_maxTryNum = 100;
|
size_t m_maxTryNum = 100;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
#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