no message

This commit is contained in:
gem
2025-02-18 15:21:31 +08:00
commit 2d133e56d7
1980 changed files with 465595 additions and 0 deletions

View File

@@ -0,0 +1,78 @@
/****************************************************************************
Copyright (c) 2022-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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 "AutoPlacement.h"
#include "base/Macros.h"
namespace cc {
namespace gi {
ccstd::vector<Vec3> AutoPlacement::generate(const PlacementInfo &info) {
switch (info.method) {
case PlaceMethod::UNIFORM:
return doGenerateUniform(info);
case PlaceMethod::ADAPTIVE:
return doGenerateAdaptive(info);
default:
CC_ABORT();
}
return {};
}
ccstd::vector<Vec3> AutoPlacement::doGenerateUniform(const PlacementInfo &info) {
if (info.nProbesX < 2U || info.nProbesY < 2U || info.nProbesZ < 2U) {
return {};
}
ccstd::vector<Vec3> probes;
Vec3 position{0.0F, 0.0F, 0.0F};
Vec3 gridSize{
(info.maxPos.x - info.minPos.x) / static_cast<float>(info.nProbesX - 1U),
(info.maxPos.y - info.minPos.y) / static_cast<float>(info.nProbesY - 1U),
(info.maxPos.z - info.minPos.z) / static_cast<float>(info.nProbesZ - 1U)};
for (auto x = 0U; x < info.nProbesX; x++) {
position.x = static_cast<float>(x) * gridSize.x + info.minPos.x;
for (auto y = 0U; y < info.nProbesY; y++) {
position.y = static_cast<float>(y) * gridSize.y + info.minPos.y;
for (auto z = 0U; z < info.nProbesZ; z++) {
position.z = static_cast<float>(z) * gridSize.z + info.minPos.z;
probes.push_back(position);
}
}
}
return probes;
}
ccstd::vector<Vec3> AutoPlacement::doGenerateAdaptive(const PlacementInfo &info) {
return doGenerateUniform(info);
}
} // namespace gi
} // namespace cc

View File

@@ -0,0 +1,58 @@
/****************************************************************************
Copyright (c) 2022-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#pragma once
#include "base/std/container/array.h"
#include "base/std/container/vector.h"
#include "math/Vec3.h"
namespace cc {
namespace gi {
enum class PlaceMethod {
UNIFORM = 0,
ADAPTIVE = 1,
};
struct PlacementInfo {
PlaceMethod method = PlaceMethod::UNIFORM;
uint32_t nProbesX{3U};
uint32_t nProbesY{3U};
uint32_t nProbesZ{3U};
Vec3 minPos{-10.0F, -10.0F, -10.0F};
Vec3 maxPos{10.0F, 10.0F, 10.0F};
};
class AutoPlacement {
public:
static ccstd::vector<Vec3> generate(const PlacementInfo &info);
private:
static ccstd::vector<Vec3> doGenerateUniform(const PlacementInfo &info);
static ccstd::vector<Vec3> doGenerateAdaptive(const PlacementInfo &info);
};
} // namespace gi
} // namespace cc

View File

@@ -0,0 +1,446 @@
/****************************************************************************
Copyright (c) 2022-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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 "Delaunay.h"
#include <algorithm>
#include "base/Log.h"
#include "core/platform/Debug.h"
#include "math/Mat3.h"
#define CC_USE_TETGEN 1
#if CC_USE_TETGEN
#include "tetgen.h"
#endif
namespace cc {
namespace gi {
void CircumSphere::init(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3) {
// calculate circumsphere of 4 points in R^3 space.
Mat3 mat(p1.x - p0.x, p1.y - p0.y, p1.z - p0.z,
p2.x - p0.x, p2.y - p0.y, p2.z - p0.z,
p3.x - p0.x, p3.y - p0.y, p3.z - p0.z);
mat.inverse();
mat.transpose();
Vec3 n(((p1.x + p0.x) * (p1.x - p0.x) + (p1.y + p0.y) * (p1.y - p0.y) + (p1.z + p0.z) * (p1.z - p0.z)) * 0.5F,
((p2.x + p0.x) * (p2.x - p0.x) + (p2.y + p0.y) * (p2.y - p0.y) + (p2.z + p0.z) * (p2.z - p0.z)) * 0.5F,
((p3.x + p0.x) * (p3.x - p0.x) + (p3.y + p0.y) * (p3.y - p0.y) + (p3.z + p0.z) * (p3.z - p0.z)) * 0.5F);
center.transformMat3(n, mat);
radiusSquared = p0.distanceSquared(center);
}
Tetrahedron::Tetrahedron(const Delaunay *delaunay, int32_t v0, int32_t v1, int32_t v2, int32_t v3 /* = -1*/)
: vertex0(v0), vertex1(v1), vertex2(v2), vertex3(v3) {
// inner tetrahedron
if (v3 >= 0) {
const auto &probes = delaunay->_probes;
const auto &p0 = probes[vertex0].position;
const auto &p1 = probes[vertex1].position;
const auto &p2 = probes[vertex2].position;
const auto &p3 = probes[vertex3].position;
sphere.init(p0, p1, p2, p3);
}
}
ccstd::vector<Tetrahedron> Delaunay::build() {
reset();
tetrahedralize();
computeAdjacency();
computeMatrices();
return std::move(_tetrahedrons);
}
void Delaunay::reset() {
_tetrahedrons.clear();
_triangles.clear();
_edges.clear();
}
#if CC_USE_TETGEN
void Delaunay::tetrahedralize() {
tetgenio in;
tetgenio out;
in.numberofpoints = static_cast<int32_t>(_probes.size());
in.pointlist = new REAL[_probes.size() * 3];
constexpr float minFloat = std::numeric_limits<float>::min();
constexpr float maxFloat = std::numeric_limits<float>::max();
Vec3 minPos = {maxFloat, maxFloat, maxFloat};
Vec3 maxPos = {minFloat, minFloat, minFloat};
for (auto i = 0; i < _probes.size(); i++) {
const auto &position = _probes[i].position;
in.pointlist[i * 3 + 0] = position.x;
in.pointlist[i * 3 + 1] = position.y;
in.pointlist[i * 3 + 2] = position.z;
minPos.x = std::min(minPos.x, position.x);
maxPos.x = std::max(maxPos.x, position.x);
minPos.y = std::min(minPos.y, position.y);
maxPos.y = std::max(maxPos.y, position.y);
minPos.z = std::min(minPos.z, position.z);
maxPos.z = std::max(maxPos.z, position.z);
}
const Vec3 center = (maxPos + minPos) * 0.5F;
tetgenbehavior options;
options.neighout = 0;
options.quiet = 1;
::tetrahedralize(&options, &in, &out);
for (auto i = 0; i < out.numberoftetrahedra; i++) {
_tetrahedrons.emplace_back(this, out.tetrahedronlist[i * 4], out.tetrahedronlist[i * 4 + 1], out.tetrahedronlist[i * 4 + 2], out.tetrahedronlist[i * 4 + 3]);
}
reorder(center);
}
#else
void Delaunay::tetrahedralize() {
// get probe count first
const auto probeCount = _probes.size();
// init a super tetrahedron containing all probes
const auto center = initTetrahedron();
for (auto i = 0; i < probeCount; i++) {
addProbe(i);
}
// remove all tetrahedrons which contain the super tetrahedron's vertices
_tetrahedrons.erase(std::remove_if(_tetrahedrons.begin(), _tetrahedrons.end(),
[probeCount](Tetrahedron &tetrahedron) {
auto vertexIndex = static_cast<int32_t>(probeCount);
return (tetrahedron.contain(vertexIndex) ||
tetrahedron.contain(vertexIndex + 1) ||
tetrahedron.contain(vertexIndex + 2) ||
tetrahedron.contain(vertexIndex + 3));
}),
_tetrahedrons.end());
// remove all additional points in the super tetrahedron
_probes.erase(_probes.begin() + probeCount, _probes.end());
reorder(center);
}
#endif
Vec3 Delaunay::initTetrahedron() {
constexpr float minFloat = std::numeric_limits<float>::min();
constexpr float maxFloat = std::numeric_limits<float>::max();
Vec3 minPos = {maxFloat, maxFloat, maxFloat};
Vec3 maxPos = {minFloat, minFloat, minFloat};
for (const auto &probe : _probes) {
const auto &position = probe.position;
minPos.x = std::min(minPos.x, position.x);
maxPos.x = std::max(maxPos.x, position.x);
minPos.y = std::min(minPos.y, position.y);
maxPos.y = std::max(maxPos.y, position.y);
minPos.z = std::min(minPos.z, position.z);
maxPos.z = std::max(maxPos.z, position.z);
}
const Vec3 center = (maxPos + minPos) * 0.5F;
const Vec3 extent = maxPos - minPos;
float offset = std::max({extent.x, extent.y, extent.z}) * 10.0F;
Vec3 p0 = center + Vec3(0.0F, offset, 0.0F);
Vec3 p1 = center + Vec3(-offset, -offset, -offset);
Vec3 p2 = center + Vec3(-offset, -offset, offset);
Vec3 p3 = center + Vec3(offset, -offset, 0.0F);
auto index = static_cast<int32_t>(_probes.size());
_probes.emplace_back(p0);
_probes.emplace_back(p1);
_probes.emplace_back(p2);
_probes.emplace_back(p3);
_tetrahedrons.emplace_back(this, index, index + 1, index + 2, index + 3);
return center;
}
void Delaunay::addTriangle(uint32_t index, int32_t tet, int32_t i, int32_t v0, int32_t v1, int32_t v2, int32_t v3) {
if (index < static_cast<uint32_t>(_triangles.size())) {
_triangles[index].set(tet, i, v0, v1, v2, v3);
} else {
_triangles.emplace_back(tet, i, v0, v1, v2, v3);
}
}
void Delaunay::addEdge(uint32_t index, int32_t tet, int32_t i, int32_t v0, int32_t v1) {
if (index < static_cast<uint32_t>(_edges.size())) {
_edges[index].set(tet, i, v0, v1);
} else {
_edges.emplace_back(tet, i, v0, v1);
}
}
void Delaunay::addProbe(int32_t vertexIndex) {
const auto &probe = _probes[vertexIndex];
const auto position = probe.position;
auto triangleIndex = 0;
for (auto i = 0; i < static_cast<int32_t>(_tetrahedrons.size()); i++) {
auto &tetrahedron = _tetrahedrons[i];
if (tetrahedron.isInCircumSphere(position)) {
tetrahedron.invalid = true;
addTriangle(triangleIndex, i, 0, tetrahedron.vertex1, tetrahedron.vertex3, tetrahedron.vertex2, tetrahedron.vertex0);
addTriangle(triangleIndex + 1, i, 1, tetrahedron.vertex0, tetrahedron.vertex2, tetrahedron.vertex3, tetrahedron.vertex1);
addTriangle(triangleIndex + 2, i, 2, tetrahedron.vertex0, tetrahedron.vertex3, tetrahedron.vertex1, tetrahedron.vertex2);
addTriangle(triangleIndex + 3, i, 3, tetrahedron.vertex0, tetrahedron.vertex1, tetrahedron.vertex2, tetrahedron.vertex3);
triangleIndex += 4;
}
}
for (auto i = 0; i < triangleIndex; i++) {
if (_triangles[i].invalid) {
continue;
}
for (auto k = i + 1; k < triangleIndex; k++) {
if (_triangles[i].isSame(_triangles[k])) {
_triangles[i].invalid = true;
_triangles[k].invalid = true;
break;
}
}
}
// remove containing tetrahedron
_tetrahedrons.erase(std::remove_if(_tetrahedrons.begin(), _tetrahedrons.end(),
[](Tetrahedron &tetrahedron) { return tetrahedron.invalid; }),
_tetrahedrons.end());
for (auto i = 0; i < triangleIndex; i++) {
const auto &triangle = _triangles[i];
if (!triangle.invalid) {
_tetrahedrons.emplace_back(this, triangle.vertex0, triangle.vertex1, triangle.vertex2, vertexIndex);
}
}
}
void Delaunay::reorder(const Vec3 &center) {
// The tetrahedron in the middle is placed at the front of the vector
std::sort(_tetrahedrons.begin(), _tetrahedrons.end(), [center](Tetrahedron &a, Tetrahedron &b) {
return a.sphere.center.distanceSquared(center) < b.sphere.center.distanceSquared(center);
});
}
void Delaunay::computeAdjacency() {
Vec3 normal;
const auto tetrahedronCount = static_cast<int32_t>(_tetrahedrons.size());
auto triangleIndex = 0;
for (auto i = 0; i < static_cast<int32_t>(_tetrahedrons.size()); i++) {
const auto &tetrahedron = _tetrahedrons[i];
addTriangle(triangleIndex, i, 0, tetrahedron.vertex1, tetrahedron.vertex3, tetrahedron.vertex2, tetrahedron.vertex0);
addTriangle(triangleIndex + 1, i, 1, tetrahedron.vertex0, tetrahedron.vertex2, tetrahedron.vertex3, tetrahedron.vertex1);
addTriangle(triangleIndex + 2, i, 2, tetrahedron.vertex0, tetrahedron.vertex3, tetrahedron.vertex1, tetrahedron.vertex2);
addTriangle(triangleIndex + 3, i, 3, tetrahedron.vertex0, tetrahedron.vertex1, tetrahedron.vertex2, tetrahedron.vertex3);
triangleIndex += 4;
}
for (auto i = 0; i < triangleIndex; i++) {
if (!_triangles[i].isOuterFace) {
continue;
}
for (auto k = i + 1; k < triangleIndex; k++) {
if (_triangles[i].isSame(_triangles[k])) {
// update adjacency between tetrahedrons
_tetrahedrons[_triangles[i].tetrahedron].neighbours[_triangles[i].index] = _triangles[k].tetrahedron;
_tetrahedrons[_triangles[k].tetrahedron].neighbours[_triangles[k].index] = _triangles[i].tetrahedron;
_triangles[i].isOuterFace = false;
_triangles[k].isOuterFace = false;
break;
}
}
if (_triangles[i].isOuterFace) {
auto &probe0 = _probes[_triangles[i].vertex0];
auto &probe1 = _probes[_triangles[i].vertex1];
auto &probe2 = _probes[_triangles[i].vertex2];
auto &probe3 = _probes[_triangles[i].vertex3];
auto edge1 = probe1.position - probe0.position;
auto edge2 = probe2.position - probe0.position;
Vec3::cross(edge1, edge2, &normal);
auto edge3 = probe3.position - probe0.position;
auto negative = normal.dot(edge3);
if (negative > 0.0F) {
normal.negate();
}
// accumulate weighted normal
probe0.normal += normal;
probe1.normal += normal;
probe2.normal += normal;
// create an outer cell with normal facing out
auto v0 = _triangles[i].vertex0;
auto v1 = negative > 0.0F ? _triangles[i].vertex2 : _triangles[i].vertex1;
auto v2 = negative > 0.0F ? _triangles[i].vertex1 : _triangles[i].vertex2;
Tetrahedron tetrahedron(this, v0, v1, v2);
// update adjacency between tetrahedron and outer cell
tetrahedron.neighbours[3] = _triangles[i].tetrahedron;
_tetrahedrons[_triangles[i].tetrahedron].neighbours[_triangles[i].index] = static_cast<int32_t>(_tetrahedrons.size());
_tetrahedrons.push_back(tetrahedron);
}
}
// start from outer cell index
auto edgeIndex = 0;
for (auto i = tetrahedronCount; i < static_cast<int32_t>(_tetrahedrons.size()); i++) {
const auto &tetrahedron = _tetrahedrons[i];
addEdge(edgeIndex, i, 0, tetrahedron.vertex1, tetrahedron.vertex2);
addEdge(edgeIndex + 1, i, 1, tetrahedron.vertex2, tetrahedron.vertex0);
addEdge(edgeIndex + 2, i, 2, tetrahedron.vertex0, tetrahedron.vertex1);
edgeIndex += 3;
}
for (auto i = 0; i < edgeIndex; i++) {
for (auto k = i + 1; k < edgeIndex; k++) {
if (_edges[i].isSame(_edges[k])) {
// update adjacency between outer cells
_tetrahedrons[_edges[i].tetrahedron].neighbours[_edges[i].index] = _edges[k].tetrahedron;
_tetrahedrons[_edges[k].tetrahedron].neighbours[_edges[k].index] = _edges[i].tetrahedron;
}
}
}
// normalize all convex hull probes' normal
for (auto &probe : _probes) {
if (!probe.normal.isZero()) {
probe.normal.normalize();
}
}
}
void Delaunay::computeMatrices() {
for (auto &tetrahedron : _tetrahedrons) {
if (tetrahedron.vertex3 >= 0) {
computeTetrahedronMatrix(tetrahedron);
} else {
computeOuterCellMatrix(tetrahedron);
}
}
}
void Delaunay::computeTetrahedronMatrix(Tetrahedron &tetrahedron) {
const auto &p0 = _probes[tetrahedron.vertex0].position;
const auto &p1 = _probes[tetrahedron.vertex1].position;
const auto &p2 = _probes[tetrahedron.vertex2].position;
const auto &p3 = _probes[tetrahedron.vertex3].position;
tetrahedron.matrix.set(
p0.x - p3.x, p1.x - p3.x, p2.x - p3.x,
p0.y - p3.y, p1.y - p3.y, p2.y - p3.y,
p0.z - p3.z, p1.z - p3.z, p2.z - p3.z);
tetrahedron.matrix.inverse();
tetrahedron.matrix.transpose();
}
void Delaunay::computeOuterCellMatrix(Tetrahedron &tetrahedron) {
Vec3 v[3];
Vec3 p[3];
v[0] = _probes[tetrahedron.vertex0].normal;
v[1] = _probes[tetrahedron.vertex1].normal;
v[2] = _probes[tetrahedron.vertex2].normal;
p[0] = _probes[tetrahedron.vertex0].position;
p[1] = _probes[tetrahedron.vertex1].position;
p[2] = _probes[tetrahedron.vertex2].position;
Vec3 a = p[0] - p[2];
Vec3 ap = v[0] - v[2];
Vec3 b = p[1] - p[2];
Vec3 bp = v[1] - v[2];
Vec3 p2 = p[2];
Vec3 cp = -v[2];
float m[12];
m[0] = ap.y * bp.z - ap.z * bp.y;
m[3] = -ap.x * bp.z + ap.z * bp.x;
m[6] = ap.x * bp.y - ap.y * bp.x;
m[9] = a.x * bp.y * cp.z - a.y * bp.x * cp.z + ap.x * b.y * cp.z - ap.y * b.x * cp.z + a.z * bp.x * cp.y - a.z * bp.y * cp.x + ap.z * b.x * cp.y - ap.z * b.y * cp.x - a.x * bp.z * cp.y + a.y * bp.z * cp.x - ap.x * b.z * cp.y + ap.y * b.z * cp.x;
m[9] -= p2.x * m[0] + p2.y * m[3] + p2.z * m[6];
m[1] = ap.y * b.z + a.y * bp.z - ap.z * b.y - a.z * bp.y;
m[4] = -a.x * bp.z - ap.x * b.z + a.z * bp.x + ap.z * b.x;
m[7] = a.x * bp.y - a.y * bp.x + ap.x * b.y - ap.y * b.x;
m[10] = a.x * b.y * cp.z - a.y * b.x * cp.z - a.x * b.z * cp.y + a.y * b.z * cp.x + a.z * b.x * cp.y - a.z * b.y * cp.x;
m[10] -= p2.x * m[1] + p2.y * m[4] + p2.z * m[7];
m[2] = -a.z * b.y + a.y * b.z;
m[5] = -a.x * b.z + a.z * b.x;
m[8] = a.x * b.y - a.y * b.x;
m[11] = 0.0F;
m[11] -= p2.x * m[2] + p2.y * m[5] + p2.z * m[8];
// coefficient of t^3
float c = ap.x * bp.y * cp.z - ap.y * bp.x * cp.z + ap.z * bp.x * cp.y - ap.z * bp.y * cp.x + ap.y * bp.z * cp.x - ap.x * bp.z * cp.y;
if (std::abs(c) > mathutils::EPSILON) {
// t^3 + p * t^2 + q * t + r = 0
for (float &k : m) {
k /= c;
}
} else {
// set last vertex index of outer cell to -2
// p * t^2 + q * t + r = 0
tetrahedron.vertex3 = -2;
}
// transpose the matrix
tetrahedron.matrix.set(m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8]);
// last column of mat3x4
tetrahedron.offset.set(m[9], m[10], m[11]);
}
} // namespace gi
} // namespace cc

View File

@@ -0,0 +1,248 @@
/****************************************************************************
Copyright (c) 2022-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#pragma once
#include "base/Macros.h"
#include "base/std/container/array.h"
#include "base/std/container/vector.h"
#include "core/geometry/AABB.h"
#include "math/Utils.h"
#include "math/Vec3.h"
namespace cc {
namespace gi {
class Delaunay;
struct Vertex {
ccstd::vector<Vec3> coefficients;
Vec3 position;
Vec3 normal;
Vertex() = default;
explicit Vertex(const Vec3 &pos)
: position(pos) {
}
};
struct Edge {
int32_t tetrahedron{-1}; // tetrahedron index this edge belongs to
int32_t index{-1}; // index in triangle's three edges of an outer cell
int32_t vertex0{-1};
int32_t vertex1{-1};
Edge() = default;
Edge(int32_t tet, int32_t i, int32_t v0, int32_t v1)
: tetrahedron(tet), index(i) {
if (v0 < v1) {
vertex0 = v0;
vertex1 = v1;
} else {
vertex0 = v1;
vertex1 = v0;
}
}
inline void set(int32_t tet, int32_t i, int32_t v0, int32_t v1) {
tetrahedron = tet;
index = i;
if (v0 < v1) {
vertex0 = v0;
vertex1 = v1;
} else {
vertex0 = v1;
vertex1 = v0;
}
}
inline bool isSame(const Edge &other) const {
return (vertex0 == other.vertex0 && vertex1 == other.vertex1);
}
};
struct Triangle {
bool invalid{false};
bool isOuterFace{true};
int32_t tetrahedron{-1}; // tetrahedron index this triangle belongs to
int32_t index{-1}; // index in tetrahedron's four triangles
int32_t vertex0{-1};
int32_t vertex1{-1};
int32_t vertex2{-1};
int32_t vertex3{-1}; // tetrahedron's last vertex index used to compute normal direction
Triangle() = default;
Triangle(int32_t tet, int32_t i, int32_t v0, int32_t v1, int32_t v2, int32_t v3)
: tetrahedron(tet), index(i), vertex3(v3) {
if (v0 < v1 && v0 < v2) {
vertex0 = v0;
if (v1 < v2) {
vertex1 = v1;
vertex2 = v2;
} else {
vertex1 = v2;
vertex2 = v1;
}
} else if (v1 < v0 && v1 < v2) {
vertex0 = v1;
if (v0 < v2) {
vertex1 = v0;
vertex2 = v2;
} else {
vertex1 = v2;
vertex2 = v0;
}
} else {
vertex0 = v2;
if (v0 < v1) {
vertex1 = v0;
vertex2 = v1;
} else {
vertex1 = v1;
vertex2 = v0;
}
}
}
inline void set(int32_t tet, int32_t i, int32_t v0, int32_t v1, int32_t v2, int32_t v3) {
invalid = false;
isOuterFace = true;
tetrahedron = tet;
index = i;
vertex3 = v3;
if (v0 < v1 && v0 < v2) {
vertex0 = v0;
if (v1 < v2) {
vertex1 = v1;
vertex2 = v2;
} else {
vertex1 = v2;
vertex2 = v1;
}
} else if (v1 < v0 && v1 < v2) {
vertex0 = v1;
if (v0 < v2) {
vertex1 = v0;
vertex2 = v2;
} else {
vertex1 = v2;
vertex2 = v0;
}
} else {
vertex0 = v2;
if (v0 < v1) {
vertex1 = v0;
vertex2 = v1;
} else {
vertex1 = v1;
vertex2 = v0;
}
}
}
inline bool isSame(const Triangle &other) const {
return (vertex0 == other.vertex0 && vertex1 == other.vertex1 && vertex2 == other.vertex2);
}
};
struct CircumSphere {
float radiusSquared{0.0F};
Vec3 center;
CircumSphere() = default;
void init(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3);
};
/**
* inner tetrahedron or outer cell structure
*/
struct Tetrahedron {
bool invalid{false};
int32_t vertex0{-1};
int32_t vertex1{-1};
int32_t vertex2{-1};
int32_t vertex3{-1}; // -1 means outer cell, otherwise inner tetrahedron
ccstd::array<int32_t, 4> neighbours{-1, -1, -1, -1};
Mat3 matrix;
Vec3 offset; // only valid in outer cell
CircumSphere sphere; // only valid in inner tetrahedron
// inner tetrahedron or outer cell constructor
Tetrahedron(const Delaunay *delaunay, int32_t v0, int32_t v1, int32_t v2, int32_t v3 = -1);
Tetrahedron() = default;
inline bool isInCircumSphere(const Vec3 &point) const {
return point.distanceSquared(sphere.center) < sphere.radiusSquared - mathutils::EPSILON;
}
inline bool contain(int32_t vertexIndex) const {
return (vertex0 == vertexIndex || vertex1 == vertexIndex ||
vertex2 == vertexIndex || vertex3 == vertexIndex);
}
inline bool isInnerTetrahedron() const {
return vertex3 >= 0;
}
inline bool isOuterCell() const {
return vertex3 < 0; // -1 or -2
}
};
class Delaunay {
public:
explicit Delaunay(ccstd::vector<Vertex> &probes) : _probes(probes) {}
~Delaunay() = default;
ccstd::vector<Tetrahedron> build();
private:
void reset();
void tetrahedralize(); // Bowyer-Watson algorithm
Vec3 initTetrahedron();
void addTriangle(uint32_t index, int32_t tet, int32_t i, int32_t v0, int32_t v1, int32_t v2, int32_t v3);
void addEdge(uint32_t index, int32_t tet, int32_t i, int32_t v0, int32_t v1);
void addProbe(int32_t vertexIndex);
void reorder(const Vec3 &center);
void computeAdjacency();
void computeMatrices();
void computeTetrahedronMatrix(Tetrahedron &tetrahedron);
void computeOuterCellMatrix(Tetrahedron &tetrahedron);
ccstd::vector<Vertex> &_probes;
ccstd::vector<Tetrahedron> _tetrahedrons;
ccstd::vector<Triangle> _triangles;
ccstd::vector<Edge> _edges;
CC_DISALLOW_COPY_MOVE_ASSIGN(Delaunay);
friend class Tetrahedron;
};
} // namespace gi
} // namespace cc

View File

@@ -0,0 +1,351 @@
/****************************************************************************
Copyright (c) 2022-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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 "LightProbe.h"
#include "PolynomialSolver.h"
#include "core/Root.h"
#include "core/scene-graph/Node.h"
#include "core/scene-graph/Scene.h"
#include "math/Math.h"
#include "math/Utils.h"
#include "renderer/pipeline/custom/RenderInterfaceTypes.h"
namespace cc {
namespace gi {
void LightProbesData::updateProbes(ccstd::vector<Vec3> &points) {
_probes.clear();
auto pointCount = points.size();
_probes.reserve(pointCount);
for (auto i = 0; i < pointCount; i++) {
_probes.emplace_back(points[i]);
}
}
void LightProbesData::updateTetrahedrons() {
Delaunay delaunay(_probes);
_tetrahedrons = delaunay.build();
}
bool LightProbesData::getInterpolationSHCoefficients(int32_t tetIndex, const Vec4 &weights, ccstd::vector<Vec3> &coefficients) const {
if (!hasCoefficients()) {
return false;
}
const auto length = SH::getBasisCount();
coefficients.resize(length);
const auto &tetrahedron = _tetrahedrons[tetIndex];
const auto &c0 = _probes[tetrahedron.vertex0].coefficients;
const auto &c1 = _probes[tetrahedron.vertex1].coefficients;
const auto &c2 = _probes[tetrahedron.vertex2].coefficients;
if (tetrahedron.vertex3 >= 0) {
const auto &c3 = _probes[tetrahedron.vertex3].coefficients;
for (auto i = 0; i < length; i++) {
coefficients[i] = c0[i] * weights.x + c1[i] * weights.y + c2[i] * weights.z + c3[i] * weights.w;
}
} else {
for (auto i = 0; i < length; i++) {
coefficients[i] = c0[i] * weights.x + c1[i] * weights.y + c2[i] * weights.z;
}
}
return true;
}
int32_t LightProbesData::getInterpolationWeights(const Vec3 &position, int32_t tetIndex, Vec4 &weights) const {
const auto tetrahedronCount = _tetrahedrons.size();
if (tetIndex < 0 || tetIndex >= tetrahedronCount) {
tetIndex = 0;
}
int32_t lastIndex = -1;
int32_t nextIndex = -1;
for (auto i = 0; i < tetrahedronCount; i++) {
const auto &tetrahedron = _tetrahedrons[tetIndex];
getBarycentricCoord(position, tetrahedron, weights);
if (weights.x >= 0.0F && weights.y >= 0.0F && weights.z >= 0.0F && weights.w >= 0.0F) {
break;
}
if (weights.x < weights.y && weights.x < weights.z && weights.x < weights.w) {
nextIndex = tetrahedron.neighbours[0];
} else if (weights.y < weights.z && weights.y < weights.w) {
nextIndex = tetrahedron.neighbours[1];
} else if (weights.z < weights.w) {
nextIndex = tetrahedron.neighbours[2];
} else {
nextIndex = tetrahedron.neighbours[3];
}
// return directly due to numerical precision error
if (lastIndex == nextIndex) {
break;
}
lastIndex = tetIndex;
tetIndex = nextIndex;
}
return tetIndex;
}
Vec3 LightProbesData::getTriangleBarycentricCoord(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &position) {
Vec3 normal;
Vec3::cross(p1 - p0, p2 - p0, &normal);
if (normal.lengthSquared() <= mathutils::EPSILON) {
return Vec3(0.0F, 0.0F, 0.0F);
}
const Vec3 n = normal.getNormalized();
const float area012Inv = 1.0F / (n.dot(normal));
Vec3 crossP12;
Vec3::cross(p1 - position, p2 - position, &crossP12);
const float areaP12 = n.dot(crossP12);
const float alpha = areaP12 * area012Inv;
Vec3 crossP20;
Vec3::cross(p2 - position, p0 - position, &crossP20);
const float areaP20 = n.dot(crossP20);
const float beta = areaP20 * area012Inv;
return Vec3(alpha, beta, 1.0F - alpha - beta);
}
void LightProbesData::getBarycentricCoord(const Vec3 &position, const Tetrahedron &tetrahedron, Vec4 &weights) const {
if (tetrahedron.vertex3 >= 0) {
getTetrahedronBarycentricCoord(position, tetrahedron, weights);
} else {
getOuterCellBarycentricCoord(position, tetrahedron, weights);
}
}
void LightProbesData::getTetrahedronBarycentricCoord(const Vec3 &position, const Tetrahedron &tetrahedron, Vec4 &weights) const {
Vec3 result = position - _probes[tetrahedron.vertex3].position;
result.transformMat3(result, tetrahedron.matrix);
weights.set(result.x, result.y, result.z, 1.0F - result.x - result.y - result.z);
}
void LightProbesData::getOuterCellBarycentricCoord(const Vec3 &position, const Tetrahedron &tetrahedron, Vec4 &weights) const {
const auto &p0 = _probes[tetrahedron.vertex0].position;
const auto &p1 = _probes[tetrahedron.vertex1].position;
const auto &p2 = _probes[tetrahedron.vertex2].position;
Vec3 normal;
const auto edge1 = p1 - p0;
const auto edge2 = p2 - p0;
Vec3::cross(edge1, edge2, &normal);
float t = Vec3::dot(position - p0, normal);
if (t < 0.0F) {
// test tetrahedron in next iterator
weights.set(0.0F, 0.0F, 0.0F, -1.0F);
return;
}
Vec3 coefficients;
coefficients.transformMat3(position, tetrahedron.matrix);
coefficients += tetrahedron.offset;
if (tetrahedron.vertex3 == -1) {
t = PolynomialSolver::getCubicUniqueRoot(coefficients.x, coefficients.y, coefficients.z);
} else {
t = PolynomialSolver::getQuadraticUniqueRoot(coefficients.x, coefficients.y, coefficients.z);
}
const auto v0 = p0 + _probes[tetrahedron.vertex0].normal * t;
const auto v1 = p1 + _probes[tetrahedron.vertex1].normal * t;
const auto v2 = p2 + _probes[tetrahedron.vertex2].normal * t;
const auto result = getTriangleBarycentricCoord(v0, v1, v2, position);
weights.set(result.x, result.y, result.z, 0.0F);
}
void LightProbes::initialize(LightProbeInfo *info) {
_giScale = info->getGIScale();
_giSamples = info->getGISamples();
_bounces = info->getBounces();
_reduceRinging = info->getReduceRinging();
_showProbe = info->isShowProbe();
_showWireframe = info->isShowWireframe();
_lightProbeSphereVolume = info->getLightProbeSphereVolume();
_showConvex = info->isShowConvex();
_data = info->getData();
}
void LightProbeInfo::activate(Scene *scene, LightProbes *resource) {
_scene = scene;
_resource = resource;
_resource->initialize(this);
}
void LightProbeInfo::onProbeBakeFinished() {
onProbeBakingChanged(_scene);
}
void LightProbeInfo::onProbeBakeCleared() {
clearSHCoefficients();
onProbeBakingChanged(_scene);
}
void LightProbeInfo::clearSHCoefficients() {
if (!_data) {
return;
}
auto &probes = _data->getProbes();
for (auto &probe : probes) {
probe.coefficients.clear();
}
clearAllSHUBOs();
}
bool LightProbeInfo::addNode(Node *node) {
if (!node) {
return false;
}
for (auto &item : _nodes) {
if (item.node == node) {
return false;
}
}
_nodes.emplace_back(node);
return true;
}
bool LightProbeInfo::removeNode(Node *node) {
if (!node) {
return false;
}
for (auto iter = _nodes.begin(); iter != _nodes.end(); ++iter) {
if (iter->node == node) {
_nodes.erase(iter);
return true;
}
}
return false;
}
void LightProbeInfo::syncData(Node *node, const ccstd::vector<Vec3> &probes) {
for (auto &item : _nodes) {
if (item.node == node) {
item.probes = probes;
return;
}
}
}
void LightProbeInfo::update(bool updateTet) {
if (!_data) {
_data = new LightProbesData();
if (_resource) {
_resource->setData(_data);
}
}
ccstd::vector<Vec3> points;
for (auto &item : _nodes) {
auto *node = item.node;
auto &probes = item.probes;
const auto &worldPosition = node->getWorldPosition();
for (auto &probe : probes) {
points.push_back(probe + worldPosition);
}
}
auto pointCount = points.size();
if (pointCount < 4) {
resetAllTetraIndices();
_data->reset();
return;
}
_data->updateProbes(points);
if (updateTet) {
resetAllTetraIndices();
_data->updateTetrahedrons();
}
}
void LightProbeInfo::onProbeBakingChanged(Node *node) { // NOLINT(misc-no-recursion)
if (!node) {
return;
}
node->emit<Node::LightProbeBakingChanged>();
const auto &children = node->getChildren();
for (const auto &child: children) {
onProbeBakingChanged(child);
}
}
void LightProbeInfo::clearAllSHUBOs() {
if (!_scene) {
return;
}
auto *renderScene = _scene->getRenderScene();
if (!renderScene) {
return;
}
for (const auto &model : renderScene->getModels()) {
model->clearSHUBOs();
}
}
void LightProbeInfo::resetAllTetraIndices() {
if (!_scene) {
return;
}
auto *renderScene = _scene->getRenderScene();
if (!renderScene) {
return;
}
for (const auto &model : renderScene->getModels()) {
model->setTetrahedronIndex(-1);
}
}
} // namespace gi
} // namespace cc

View File

@@ -0,0 +1,280 @@
/****************************************************************************
Copyright (c) 2022-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#pragma once
#include "Delaunay.h"
#include "SH.h"
#include "base/Macros.h"
#include "base/Ptr.h"
#include "base/RefCounted.h"
#include "base/std/container/vector.h"
#include "math/Vec3.h"
#include "math/Vec4.h"
namespace cc {
class Scene;
class Node;
namespace gi {
class LightProbeInfo;
class LightProbesData : public RefCounted {
public:
LightProbesData() = default;
inline ccstd::vector<Vertex> &getProbes() { return _probes; }
inline void setProbes(const ccstd::vector<Vertex> &probes) { _probes = probes; }
inline ccstd::vector<Tetrahedron> &getTetrahedrons() { return _tetrahedrons; }
inline void setTetrahedrons(const ccstd::vector<Tetrahedron> &tetrahedrons) { _tetrahedrons = tetrahedrons; }
inline bool empty() const { return _probes.empty() || _tetrahedrons.empty(); }
inline void reset() {
_probes.clear();
_tetrahedrons.clear();
}
void updateProbes(ccstd::vector<Vec3> &points);
void updateTetrahedrons();
inline bool hasCoefficients() const { return !empty() && !_probes[0].coefficients.empty(); }
bool getInterpolationSHCoefficients(int32_t tetIndex, const Vec4 &weights, ccstd::vector<Vec3> &coefficients) const;
int32_t getInterpolationWeights(const Vec3 &position, int32_t tetIndex, Vec4 &weights) const;
private:
static Vec3 getTriangleBarycentricCoord(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &position);
void getBarycentricCoord(const Vec3 &position, const Tetrahedron &tetrahedron, Vec4 &weights) const;
void getTetrahedronBarycentricCoord(const Vec3 &position, const Tetrahedron &tetrahedron, Vec4 &weights) const;
void getOuterCellBarycentricCoord(const Vec3 &position, const Tetrahedron &tetrahedron, Vec4 &weights) const;
public:
ccstd::vector<Vertex> _probes;
ccstd::vector<Tetrahedron> _tetrahedrons;
};
class LightProbes final {
public:
LightProbes() = default;
~LightProbes() = default;
void initialize(LightProbeInfo *info);
inline bool empty() const {
if (!_data) {
return true;
}
return _data->empty();
}
inline void setGIScale(float val) { _giScale = val; }
inline float getGIScale() const { return _giScale; }
inline void setLightProbeSphereVolume(float val) { _lightProbeSphereVolume = val; }
inline float getLightProbeSphereVolume() const { return _lightProbeSphereVolume; }
inline void setGISamples(uint32_t val) { _giSamples = val; }
inline uint32_t getGISamples() const { return _giSamples; }
inline void setBounces(uint32_t val) { _bounces = val; }
inline uint32_t getBounces() const { return _bounces; }
inline void setReduceRinging(float val) { _reduceRinging = val; }
inline float getReduceRinging() const { return _reduceRinging; }
inline void setShowProbe(bool val) { _showProbe = val; }
inline bool isShowProbe() const { return _showProbe; }
inline void setShowWireframe(bool val) { _showWireframe = val; }
inline bool isShowWireframe() const { return _showWireframe; }
inline void setShowConvex(bool val) { _showConvex = val; }
inline bool isShowConvex() const { return _showConvex; }
inline void setData(LightProbesData *data) { _data = data; }
inline LightProbesData *getData() const { return _data.get(); }
float _giScale{1.0F};
float _lightProbeSphereVolume{1.0F};
uint32_t _giSamples{1024U};
uint32_t _bounces{2U};
float _reduceRinging{0.0F};
bool _showProbe{true};
bool _showWireframe{true};
bool _showConvex{false};
IntrusivePtr<LightProbesData> _data;
};
struct ILightProbeNode {
Node *node{nullptr};
ccstd::vector<Vec3> probes;
explicit ILightProbeNode(Node *n)
: node(n) {}
};
class LightProbeInfo : public RefCounted {
public:
LightProbeInfo() = default;
~LightProbeInfo() override = default;
void activate(Scene *scene, LightProbes *resource);
void onProbeBakeFinished();
void onProbeBakeCleared();
void clearSHCoefficients();
inline bool isUniqueNode() const { return _nodes.size() == 1; }
bool addNode(Node *node);
bool removeNode(Node *node);
void syncData(Node *node, const ccstd::vector<Vec3> &probes);
void update(bool updateTet = true);
inline void setGIScale(float val) {
if (_giScale == val) {
return;
}
_giScale = val;
if (_resource) {
_resource->setGIScale(val);
}
}
inline float getGIScale() const { return _giScale; }
inline void setLightProbeSphereVolume(float val) {
if (_lightProbeSphereVolume == val) {
return;
}
_lightProbeSphereVolume = val;
if (_resource) {
_resource->setLightProbeSphereVolume(val);
}
}
inline float getLightProbeSphereVolume() const { return _lightProbeSphereVolume; }
inline void setGISamples(uint32_t val) {
if (_giSamples == val) {
return;
}
_giSamples = val;
if (_resource) {
_resource->setGISamples(val);
}
}
inline uint32_t getGISamples() const { return _giSamples; }
inline void setBounces(uint32_t val) {
if (_bounces == val) {
return;
}
_bounces = val;
if (_resource) {
_resource->setBounces(val);
}
}
inline uint32_t getBounces() const { return _bounces; }
inline void setReduceRinging(float val) {
if (_reduceRinging == val) {
return;
}
_reduceRinging = val;
if (_resource) {
_resource->setReduceRinging(val);
}
}
inline float getReduceRinging() const { return _reduceRinging; }
inline void setShowProbe(bool val) {
if (_showProbe == val) {
return;
}
_showProbe = val;
if (_resource) {
_resource->setShowProbe(val);
}
}
inline bool isShowProbe() const { return _showProbe; }
inline void setShowWireframe(bool val) {
if (_showWireframe == val) {
return;
}
_showWireframe = val;
if (_resource) {
_resource->setShowWireframe(val);
}
}
inline bool isShowWireframe() const { return _showWireframe; }
inline void setShowConvex(bool val) {
if (_showConvex == val) {
return;
}
_showConvex = val;
if (_resource) {
_resource->setShowConvex(val);
}
}
inline bool isShowConvex() const { return _showConvex; }
inline void setData(LightProbesData *data) {
_data = data;
if (_resource) {
_resource->setData(data);
}
}
inline LightProbesData *getData() const { return _data.get(); }
//cjh JSB need to bind the property, so need to make it public
float _giScale{1.0F};
float _lightProbeSphereVolume{1.0F};
uint32_t _giSamples{1024U};
uint32_t _bounces{2U};
float _reduceRinging{0.0F};
bool _showProbe{true};
bool _showWireframe{true};
bool _showConvex{false};
IntrusivePtr<LightProbesData> _data;
private:
void onProbeBakingChanged(Node *node);
void clearAllSHUBOs();
void resetAllTetraIndices();
Scene *_scene{nullptr};
ccstd::vector<ILightProbeNode> _nodes;
LightProbes *_resource{nullptr};
};
} // namespace gi
} // namespace cc

View File

@@ -0,0 +1,93 @@
/****************************************************************************
Copyright (c) 2022-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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 "PolynomialSolver.h"
#include <cmath>
#include "base/std/container/vector.h"
#include "math/Math.h"
namespace cc {
namespace gi {
float PolynomialSolver::getQuadraticUniqueRoot(float b, float c, float d) {
// quadratic case
if (b != 0.0F) {
// the discriminant should be 0
return -c / (2.0F * b);
}
// linear case
if (c != 0.0F) {
return -d / c;
}
// never reach here
return 0.0F;
}
float PolynomialSolver::getCubicUniqueRoot(float b, float c, float d) {
ccstd::vector<float> roots;
// let x = y - b / 3, convert equation to: y^3 + 3 * p * y + 2 * q = 0
// where p = c / 3 - b^2 / 9, q = d / 2 + b^3 / 27 - b * c / 6
const auto offset = -b / 3.0F;
const auto p = c / 3.0F - (b * b) / 9.0F;
const auto q = d / 2.0F + (b * b * b) / 27.0F - (b * c) / 6.0F;
const auto delta = p * p * p + q * q; // discriminant
if (delta > 0.0F) {
// only one real root
const auto sqrtDelta = std::sqrt(delta);
roots.push_back(std::cbrt(-q + sqrtDelta) + std::cbrt(-q - sqrtDelta));
} else if (delta < 0.0F) {
// three different real roots
const auto angle = std::acos(-q * std::sqrt(-p) / (p * p)) / 3.0F;
roots.push_back(2.0F * std::sqrt(-p) * std::cos(angle));
roots.push_back(2.0F * std::sqrt(-p) * std::cos(angle + 2.0F * math::PI / 3.0F));
roots.push_back(2.0F * std::sqrt(-p) * std::cos(angle + 4.0F * math::PI / 3.0F));
} else {
// three real roots, at least two equal roots
if (q == 0.0F) {
roots.push_back(0.0F);
} else {
const auto root = std::cbrt(q);
roots.push_back(root);
roots.push_back(-2.0F * root);
}
}
// return the unique positive root
for (float root : roots) {
if (root + offset >= 0.0F) {
return root + offset;
}
}
// never reach here
return 0.0;
}
} // namespace gi
} // namespace cc

View File

@@ -0,0 +1,47 @@
/****************************************************************************
Copyright (c) 2022-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#pragma once
namespace cc {
namespace gi {
class PolynomialSolver {
public:
/**
* solve quadratic equation: b * t^2 + c * t + d = 0
* return the unique real positive root
*/
static float getQuadraticUniqueRoot(float b, float c, float d);
/**
* solve cubic equation: t^3 + b * t^2 + c * t + d = 0
* return the unique real positive root
*/
static float getCubicUniqueRoot(float b, float c, float d);
};
} // namespace gi
} // namespace cc

249
cocos/gi/light-probe/SH.cpp Normal file
View File

@@ -0,0 +1,249 @@
/****************************************************************************
Copyright (c) 2022-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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 "SH.h"
#include "base/Macros.h"
namespace cc {
namespace gi {
Vec3 LightProbeSampler::uniformSampleSphere(float u1, float u2) {
float z = 1.0F - 2.0F * u1;
float r = std::sqrt(std::max(0.0F, 1.0F - z * z));
float phi = 2.0F * math::PI * u2;
float x = r * std::cos(phi);
float y = r * std::sin(phi);
return Vec3(x, y, z);
}
ccstd::vector<Vec3> LightProbeSampler::uniformSampleSphereAll(uint32_t sampleCount) {
CC_ASSERT_GT(sampleCount, 0U);
const auto uCount1 = static_cast<uint32_t>(std::sqrt(sampleCount));
const auto uCount2 = uCount1;
ccstd::vector<Vec3> samples;
const auto uDelta1 = 1.0F / static_cast<float>(uCount1);
const auto uDelta2 = 1.0F / static_cast<float>(uCount2);
for (auto i = 0U; i < uCount1; i++) {
const auto u1 = (static_cast<float>(i) + 0.5F) * uDelta1;
for (auto j = 0U; j < uCount2; j++) {
const auto u2 = (static_cast<float>(j) + 0.5F) * uDelta2;
const auto sample = uniformSampleSphere(u1, u2);
samples.push_back(sample);
}
}
return samples;
}
ccstd::vector<SH::BasisFunction> SH::basisFunctions = {
[](const Vec3& /*v*/) -> float { return 0.282095F; }, // 0.5F * std::sqrt(math::PI_INV)
[](const Vec3& v) -> float { return 0.488603F * v.y; }, // 0.5F * std::sqrt(3.0F * math::PI_INV) * v.y
[](const Vec3& v) -> float { return 0.488603F * v.z; }, // 0.5F * std::sqrt(3.0F * math::PI_INV) * v.z
[](const Vec3& v) -> float { return 0.488603F * v.x; }, // 0.5F * std::sqrt(3.0F * math::PI_INV) * v.x
[](const Vec3& v) -> float { return 1.09255F * v.y * v.x; }, // 0.5F * std::sqrt(15.0F * math::PI_INV) * v.y * v.x
[](const Vec3& v) -> float { return 1.09255F * v.y * v.z; }, // 0.5F * std::sqrt(15.0F * math::PI_INV) * v.y * v.z
[](const Vec3& v) -> float { return 0.946175F * (v.z * v.z - 1.0F / 3.0F); }, // 0.75F * std::sqrt(5.0F * math::PI_INV) * (v.z * v.z - 1.0F / 3.0F)
[](const Vec3& v) -> float { return 1.09255F * v.z * v.x; }, // 0.5F * std::sqrt(15.0F * math::PI_INV) * v.z * v.x
[](const Vec3& v) -> float { return 0.546274F * (v.x * v.x - v.y * v.y); }, // 0.25F * std::sqrt(15.0F * math::PI_INV) * (v.x * v.x - v.y * v.y)
};
ccstd::vector<float> SH::basisOverPI = {
0.0897936F, // 0.282095 / Math.PI
0.155527F, // 0.488603 / Math.PI
0.155527F, // 0.488603 / Math.PI
0.155527F, // 0.488603 / Math.PI
0.347769F, // 1.09255 / Math.PI
0.347769F, // 1.09255 / Math.PI
0.301177F, // 0.946175 / Math.PI
0.347769F, // 1.09255 / Math.PI
0.173884F, // 0.546274 / Math.PI
};
void SH::updateUBOData(Float32Array& data, int32_t offset, ccstd::vector<Vec3>& coefficients) {
// cc_sh_linear_const_r
data[offset++] = coefficients[3].x * basisOverPI[3];
data[offset++] = coefficients[1].x * basisOverPI[1];
data[offset++] = coefficients[2].x * basisOverPI[2];
data[offset++] = coefficients[0].x * basisOverPI[0] - coefficients[6].x * basisOverPI[6] / 3.0F;
// cc_sh_linear_const_g
data[offset++] = coefficients[3].y * basisOverPI[3];
data[offset++] = coefficients[1].y * basisOverPI[1];
data[offset++] = coefficients[2].y * basisOverPI[2];
data[offset++] = coefficients[0].y * basisOverPI[0] - coefficients[6].y * basisOverPI[6] / 3.0F;
// cc_sh_linear_const_b
data[offset++] = coefficients[3].z * basisOverPI[3];
data[offset++] = coefficients[1].z * basisOverPI[1];
data[offset++] = coefficients[2].z * basisOverPI[2];
data[offset++] = coefficients[0].z * basisOverPI[0] - coefficients[6].z * basisOverPI[6] / 3.0F;
// cc_sh_quadratic_r
data[offset++] = coefficients[4].x * basisOverPI[4];
data[offset++] = coefficients[5].x * basisOverPI[5];
data[offset++] = coefficients[6].x * basisOverPI[6];
data[offset++] = coefficients[7].x * basisOverPI[7];
// cc_sh_quadratic_g
data[offset++] = coefficients[4].y * basisOverPI[4];
data[offset++] = coefficients[5].y * basisOverPI[5];
data[offset++] = coefficients[6].y * basisOverPI[6];
data[offset++] = coefficients[7].y * basisOverPI[7];
// cc_sh_quadratic_b
data[offset++] = coefficients[4].z * basisOverPI[4];
data[offset++] = coefficients[5].z * basisOverPI[5];
data[offset++] = coefficients[6].z * basisOverPI[6];
data[offset++] = coefficients[7].z * basisOverPI[7];
// cc_sh_quadratic_a
data[offset++] = coefficients[8].x * basisOverPI[8];
data[offset++] = coefficients[8].y * basisOverPI[8];
data[offset++] = coefficients[8].z * basisOverPI[8];
data[offset++] = 0.0;
}
Vec3 SH::shaderEvaluate(const Vec3& normal, ccstd::vector<Vec3>& coefficients) {
const Vec4 linearConstR = {
coefficients[3].x * basisOverPI[3],
coefficients[1].x * basisOverPI[1],
coefficients[2].x * basisOverPI[2],
coefficients[0].x * basisOverPI[0] - coefficients[6].x * basisOverPI[6] / 3.0F};
const Vec4 linearConstG = {
coefficients[3].y * basisOverPI[3],
coefficients[1].y * basisOverPI[1],
coefficients[2].y * basisOverPI[2],
coefficients[0].y * basisOverPI[0] - coefficients[6].y * basisOverPI[6] / 3.0F};
const Vec4 linearConstB = {
coefficients[3].z * basisOverPI[3],
coefficients[1].z * basisOverPI[1],
coefficients[2].z * basisOverPI[2],
coefficients[0].z * basisOverPI[0] - coefficients[6].z * basisOverPI[6] / 3.0F};
const Vec4 quadraticR = {
coefficients[4].x * basisOverPI[4],
coefficients[5].x * basisOverPI[5],
coefficients[6].x * basisOverPI[6],
coefficients[7].x * basisOverPI[7]};
const Vec4 quadraticG = {
coefficients[4].y * basisOverPI[4],
coefficients[5].y * basisOverPI[5],
coefficients[6].y * basisOverPI[6],
coefficients[7].y * basisOverPI[7]};
const Vec4 quadraticB = {
coefficients[4].z * basisOverPI[4],
coefficients[5].z * basisOverPI[5],
coefficients[6].z * basisOverPI[6],
coefficients[7].z * basisOverPI[7]};
const Vec3 quadraticA = {
coefficients[8].x * basisOverPI[8],
coefficients[8].y * basisOverPI[8],
coefficients[8].z * basisOverPI[8]};
Vec3 result{0.0F, 0.0F, 0.0F};
Vec4 normal4{normal.x, normal.y, normal.z, 1.0F};
// calculate linear and const terms
result.x = linearConstR.dot(normal4);
result.y = linearConstG.dot(normal4);
result.z = linearConstB.dot(normal4);
// calculate quadratic terms
Vec4 n14{normal.x * normal.y, normal.y * normal.z, normal.z * normal.z, normal.z * normal.x};
float n5 = normal.x * normal.x - normal.y * normal.y;
result.x += quadraticR.dot(n14);
result.y += quadraticG.dot(n14);
result.z += quadraticB.dot(n14);
result += quadraticA * n5;
return result;
}
Vec3 SH::evaluate(const Vec3& sample, const ccstd::vector<Vec3>& coefficients) {
Vec3 result{0.0F, 0.0F, 0.0F};
const auto size = coefficients.size();
for (auto i = 0; i < size; i++) {
const Vec3& c = coefficients[i];
result += c * evaluateBasis(i, sample);
}
return result;
}
ccstd::vector<Vec3> SH::project(const ccstd::vector<Vec3>& samples, const ccstd::vector<Vec3>& values) {
CC_ASSERT(!samples.empty() && samples.size() == values.size());
// integral using Monte Carlo method
const auto basisCount = getBasisCount();
const auto sampleCount = samples.size();
const auto scale = 1.0F / (LightProbeSampler::uniformSpherePdf() * static_cast<float>(sampleCount));
ccstd::vector<Vec3> coefficients;
coefficients.reserve(basisCount);
for (auto i = 0; i < basisCount; i++) {
Vec3 coefficient{0.0F, 0.0F, 0.0F};
for (auto k = 0; k < sampleCount; k++) {
coefficient += values[k] * evaluateBasis(i, samples[k]);
}
coefficient *= scale;
coefficients.push_back(coefficient);
}
return coefficients;
}
ccstd::vector<Vec3> SH::convolveCosine(const ccstd::vector<Vec3>& radianceCoefficients) {
static const float COS_THETA[3] = {0.8862268925F, 1.0233267546F, 0.4954159260F};
ccstd::vector<Vec3> irradianceCoefficients;
for (auto l = 0; l <= LMAX; l++) {
for (auto m = -l; m <= l; m++) {
auto i = toIndex(l, m);
Vec3 coefficient = lambda(l) * COS_THETA[l] * radianceCoefficients[i];
irradianceCoefficients.push_back(coefficient);
}
}
return irradianceCoefficients;
}
} // namespace gi
} // namespace cc

137
cocos/gi/light-probe/SH.h Normal file
View File

@@ -0,0 +1,137 @@
/****************************************************************************
Copyright (c) 2022-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#pragma once
#include <cmath>
#include <functional>
#include "base/std/container/vector.h"
#include "core/TypedArray.h"
#include "math/Math.h"
#include "math/Vec3.h"
namespace cc {
namespace gi {
#define SH_BASIS_COUNT 9
class LightProbeSampler {
public:
/**
* generate one sample from sphere uniformly
*/
static Vec3 uniformSampleSphere(float u1, float u2);
/**
* generate ucount1 * ucount2 samples from sphere uniformly
*/
static ccstd::vector<Vec3> uniformSampleSphereAll(uint32_t sampleCount);
/**
* probability density function of uniform distribution on spherical surface
*/
static inline float uniformSpherePdf() { return 1.0F / (4.0F * math::PI); }
};
/**
* Spherical Harmonics utility class
*/
class SH {
public:
using BasisFunction = std::function<float(const Vec3& v)>;
/**
* update ubo data by coefficients
*/
static void updateUBOData(Float32Array& data, int32_t offset, ccstd::vector<Vec3>& coefficients);
/**
* recreate a function from sh coefficients, which is same as SHEvaluate in shader
*/
static Vec3 shaderEvaluate(const Vec3& normal, ccstd::vector<Vec3>& coefficients);
/**
* recreate a function from sh coefficients
*/
static Vec3 evaluate(const Vec3& sample, const ccstd::vector<Vec3>& coefficients);
/**
* project a function to sh coefficients
*/
static ccstd::vector<Vec3> project(const ccstd::vector<Vec3>& samples, const ccstd::vector<Vec3>& values);
/**
* calculate irradiance's sh coefficients from radiance's sh coefficients directly
*/
static ccstd::vector<Vec3> convolveCosine(const ccstd::vector<Vec3>& radianceCoefficients);
/**
* return basis function count
*/
static inline uint32_t getBasisCount() {
return SH_BASIS_COUNT;
}
/**
* evaluate from a basis function
*/
static inline float evaluateBasis(uint32_t index, const Vec3& sample) {
CC_ASSERT(index < getBasisCount());
const auto& func = basisFunctions[index];
return func(sample);
}
static inline void reduceRinging(ccstd::vector<Vec3>& coefficients, float lambda) {
if (lambda == 0.0F) {
return;
}
for (int32_t l = 0; l <= LMAX; ++l) {
auto level = static_cast<float>(l);
float scale = 1.0F / (1.0F + lambda * level * level * (level + 1) * (level + 1));
for (int32_t m = -l; m <= l; ++m) {
const int32_t i = toIndex(l, m);
coefficients[i] *= scale;
}
}
}
private:
static inline float lambda(int32_t l) {
return std::sqrt((4.0F * math::PI) / (2.0F * static_cast<float>(l) + 1.0F));
}
static inline int32_t toIndex(int32_t l, int32_t m) {
return l * l + l + m;
}
static constexpr int32_t LMAX = 2;
static ccstd::vector<BasisFunction> basisFunctions;
static ccstd::vector<float> basisOverPI;
};
} // namespace gi
} // namespace cc

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff