no message
This commit is contained in:
125
cocos/3d/misc/Buffer.cpp
Normal file
125
cocos/3d/misc/Buffer.cpp
Normal file
@@ -0,0 +1,125 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2021-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 "3d/misc/Buffer.h"
|
||||
#include "base/std/variant.h"
|
||||
|
||||
namespace cc {
|
||||
|
||||
namespace {
|
||||
ccstd::unordered_map<gfx::FormatType, ccstd::string> typeMap{
|
||||
{gfx::FormatType::UNORM, "Uint"},
|
||||
{gfx::FormatType::SNORM, "Int"},
|
||||
{gfx::FormatType::UINT, "Uint"},
|
||||
{gfx::FormatType::INT, "Int"},
|
||||
{gfx::FormatType::UFLOAT, "Float"},
|
||||
{gfx::FormatType::FLOAT, "Float"},
|
||||
};
|
||||
|
||||
ccstd::string getDataViewType(const gfx::FormatInfo &info) {
|
||||
ccstd::string type;
|
||||
auto iter = typeMap.find(info.type);
|
||||
if (iter != typeMap.end()) {
|
||||
type = iter->second;
|
||||
} else {
|
||||
type = "Uint";
|
||||
}
|
||||
|
||||
const uint32_t bytes = info.size / info.count * 8;
|
||||
return type + std::to_string(bytes);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
using DataVariant = ccstd::variant<ccstd::monostate, int32_t, float>;
|
||||
using MapBufferCallback = std::function<DataVariant(const DataVariant &cur, uint32_t idx, const DataView &view)>;
|
||||
|
||||
DataView mapBuffer(DataView &target,
|
||||
const MapBufferCallback &callback,
|
||||
ccstd::optional<gfx::Format> aFormat,
|
||||
ccstd::optional<uint32_t> aOffset,
|
||||
ccstd::optional<uint32_t> aLength,
|
||||
ccstd::optional<uint32_t> aStride,
|
||||
DataView *out) {
|
||||
gfx::Format format = aFormat.has_value() ? aFormat.value() : gfx::Format::R32F;
|
||||
uint32_t offset = aOffset.has_value() ? aOffset.value() : 0;
|
||||
uint32_t length = aLength.has_value() ? aLength.value() : target.byteLength() - offset;
|
||||
uint32_t stride = aStride.has_value() ? aStride.value() : 0;
|
||||
|
||||
DataView dataView;
|
||||
if (out == nullptr) {
|
||||
out = &dataView;
|
||||
dataView.assign(target.buffer()->slice(target.byteOffset(), target.byteOffset() + target.byteLength()));
|
||||
}
|
||||
|
||||
const auto &info = gfx::GFX_FORMAT_INFOS[static_cast<int32_t>(format)];
|
||||
if (stride == 0) {
|
||||
stride = info.size;
|
||||
}
|
||||
|
||||
static const ccstd::string SET_PREFIX{"set"};
|
||||
static const ccstd::string GET_PREFIX{"get"};
|
||||
|
||||
bool isFloat = info.type == gfx::FormatType::FLOAT || info.type == gfx::FormatType::UFLOAT;
|
||||
DataView::IntWritter intWritter = nullptr;
|
||||
if (!isFloat) {
|
||||
intWritter = DataView::intWritterMap[SET_PREFIX + getDataViewType(info)];
|
||||
}
|
||||
|
||||
DataView::ReaderVariant intReader;
|
||||
if (!isFloat) {
|
||||
intReader = DataView::intReaderMap[GET_PREFIX + getDataViewType(info)];
|
||||
}
|
||||
|
||||
const uint32_t componentBytesLength = info.size / info.count;
|
||||
const uint32_t nSeg = floor(length / stride);
|
||||
|
||||
for (uint32_t iSeg = 0; iSeg < nSeg; ++iSeg) {
|
||||
const uint32_t x = offset + stride * iSeg;
|
||||
for (uint32_t iComponent = 0; iComponent < info.count; ++iComponent) {
|
||||
const uint32_t y = x + componentBytesLength * iComponent;
|
||||
if (isFloat) {
|
||||
float cur = target.getFloat32(y);
|
||||
auto dataVariant = callback(cur, iComponent, target);
|
||||
if (ccstd::holds_alternative<float>(dataVariant)) {
|
||||
out->setFloat32(y, ccstd::get<float>(dataVariant));
|
||||
} else {
|
||||
CC_LOG_ERROR("mapBuffer, wrong data type, expect float");
|
||||
}
|
||||
} else {
|
||||
int32_t cur = target.readInt(intReader, y);
|
||||
// iComponent is usually more useful than y
|
||||
auto dataVariant = callback(cur, iComponent, target);
|
||||
if (ccstd::holds_alternative<int32_t>(dataVariant)) {
|
||||
(target.*intWritter)(y, ccstd::get<int32_t>(dataVariant));
|
||||
} else {
|
||||
CC_LOG_ERROR("mapBuffer, wrong data type, expect int32_t");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return dataView;
|
||||
}
|
||||
|
||||
} // namespace cc
|
||||
125
cocos/3d/misc/Buffer.h
Normal file
125
cocos/3d/misc/Buffer.h
Normal file
@@ -0,0 +1,125 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2021-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 "base/std/optional.h"
|
||||
#include "core/DataView.h"
|
||||
#include "renderer/gfx-base/GFXDef.h"
|
||||
|
||||
namespace cc {
|
||||
|
||||
// default params behaviors just like on an plain, compact Float32Array
|
||||
template <typename T>
|
||||
void writeBuffer(DataView &target,
|
||||
const ccstd::vector<T> &data,
|
||||
const gfx::Format &format = gfx::Format::R32F,
|
||||
uint32_t offset = 0,
|
||||
uint32_t stride = 0) {
|
||||
const gfx::FormatInfo &info = gfx::GFX_FORMAT_INFOS[static_cast<uint32_t>(format)];
|
||||
if (stride == 0) {
|
||||
stride = info.size;
|
||||
}
|
||||
const uint32_t componentBytesLength = info.size / info.count;
|
||||
const auto nSeg = static_cast<uint32_t>(floor(data.size() / info.count));
|
||||
|
||||
const uint32_t bytes = info.size / info.count * 8;
|
||||
|
||||
for (uint32_t iSeg = 0; iSeg < nSeg; ++iSeg) {
|
||||
uint32_t x = offset + stride * iSeg;
|
||||
for (uint32_t iComponent = 0; iComponent < info.count; ++iComponent) {
|
||||
const uint32_t y = x + componentBytesLength * iComponent;
|
||||
// default Little-Endian
|
||||
switch (info.type) {
|
||||
case gfx::FormatType::UINT:
|
||||
case gfx::FormatType::UNORM:
|
||||
switch (bytes) {
|
||||
case 8:
|
||||
target.setUint8(y, static_cast<uint8_t>(data[info.count * iSeg + iComponent]));
|
||||
break;
|
||||
case 16:
|
||||
target.setUint16(y, static_cast<uint16_t>(data[info.count * iSeg + iComponent]));
|
||||
break;
|
||||
case 32:
|
||||
target.setUint32(y, static_cast<uint32_t>(data[info.count * iSeg + iComponent]));
|
||||
break;
|
||||
default:
|
||||
CC_ABORT();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case gfx::FormatType::INT:
|
||||
case gfx::FormatType::SNORM:
|
||||
switch (bytes) {
|
||||
case 8:
|
||||
target.setInt8(y, static_cast<int8_t>(data[info.count * iSeg + iComponent]));
|
||||
break;
|
||||
case 16:
|
||||
target.setInt16(y, static_cast<int16_t>(data[info.count * iSeg + iComponent]));
|
||||
break;
|
||||
case 32:
|
||||
target.setInt32(y, static_cast<int32_t>(data[info.count * iSeg + iComponent]));
|
||||
break;
|
||||
default:
|
||||
CC_ABORT();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case gfx::FormatType::UFLOAT:
|
||||
case gfx::FormatType::FLOAT:
|
||||
switch (bytes) {
|
||||
case 8:
|
||||
target.setFloat32(y, static_cast<float>(data[info.count * iSeg + iComponent]));
|
||||
break;
|
||||
case 16:
|
||||
target.setFloat32(y, static_cast<float>(data[info.count * iSeg + iComponent]));
|
||||
break;
|
||||
case 32:
|
||||
target.setFloat32(y, static_cast<float>(data[info.count * iSeg + iComponent]));
|
||||
break;
|
||||
default:
|
||||
CC_ABORT();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
CC_ABORT();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
using DataVariant = ccstd::variant<ccstd::monostate, int32_t, float>;
|
||||
using MapBufferCallback = std::function<DataVariant(const DataVariant &cur, uint32_t idx, const DataView &view)>;
|
||||
|
||||
DataView mapBuffer(DataView &target,
|
||||
const MapBufferCallback &callback,
|
||||
ccstd::optional<gfx::Format> aFormat,
|
||||
ccstd::optional<uint32_t> aOffset,
|
||||
ccstd::optional<uint32_t> aLength,
|
||||
ccstd::optional<uint32_t> aStride,
|
||||
DataView *out);
|
||||
|
||||
} // namespace cc
|
||||
63
cocos/3d/misc/BufferBlob.cpp
Normal file
63
cocos/3d/misc/BufferBlob.cpp
Normal file
@@ -0,0 +1,63 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2021-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 "3d/misc/BufferBlob.h"
|
||||
#include "core/TypedArray.h"
|
||||
|
||||
namespace cc {
|
||||
|
||||
void BufferBlob::setNextAlignment(uint32_t align) {
|
||||
if (align != 0) {
|
||||
const uint32_t remainder = _length % align;
|
||||
if (remainder != 0) {
|
||||
const uint32_t padding = align - remainder;
|
||||
_arrayBufferOrPaddings.emplace_back(padding);
|
||||
_length += padding;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t BufferBlob::addBuffer(ArrayBuffer *arrayBuffer) {
|
||||
const uint32_t result = _length;
|
||||
_arrayBufferOrPaddings.emplace_back(arrayBuffer);
|
||||
_length += arrayBuffer->byteLength();
|
||||
return result;
|
||||
}
|
||||
|
||||
ArrayBuffer::Ptr BufferBlob::getCombined() {
|
||||
Int8Array result(_length);
|
||||
uint32_t counter = 0;
|
||||
|
||||
for (const auto &arrayBufferOrPadding : _arrayBufferOrPaddings) {
|
||||
if (const auto *p = ccstd::get_if<uint32_t>(&arrayBufferOrPadding)) {
|
||||
counter += *p;
|
||||
} else if (const auto *p = ccstd::get_if<ArrayBuffer::Ptr>(&arrayBufferOrPadding)) {
|
||||
result.set(*p, counter);
|
||||
counter += (*p)->byteLength();
|
||||
}
|
||||
}
|
||||
|
||||
return result.buffer();
|
||||
}
|
||||
|
||||
} // namespace cc
|
||||
47
cocos/3d/misc/BufferBlob.h
Normal file
47
cocos/3d/misc/BufferBlob.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2021-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/variant.h"
|
||||
#include "cocos/base/std/container/vector.h"
|
||||
#include "core/ArrayBuffer.h"
|
||||
|
||||
namespace cc {
|
||||
|
||||
class BufferBlob {
|
||||
public:
|
||||
void setNextAlignment(uint32_t align);
|
||||
|
||||
uint32_t addBuffer(ArrayBuffer *arrayBuffer);
|
||||
|
||||
inline uint32_t getLength() const { return _length; }
|
||||
|
||||
ArrayBuffer::Ptr getCombined();
|
||||
|
||||
private:
|
||||
ccstd::vector<ccstd::variant<ccstd::monostate, ArrayBuffer::Ptr, uint32_t>> _arrayBufferOrPaddings;
|
||||
uint32_t _length{0};
|
||||
};
|
||||
|
||||
} // namespace cc
|
||||
483
cocos/3d/misc/CreateMesh.cpp
Normal file
483
cocos/3d/misc/CreateMesh.cpp
Normal file
@@ -0,0 +1,483 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2022-2023 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
https://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 "3d/misc/CreateMesh.h"
|
||||
#include <zlib.h>
|
||||
#include <algorithm>
|
||||
#include "3d/misc/Buffer.h"
|
||||
#include "3d/misc/BufferBlob.h"
|
||||
#include "core/ArrayBuffer.h"
|
||||
#include "core/DataView.h"
|
||||
#include "core/assets/RenderingSubMesh.h"
|
||||
#include "meshopt/meshoptimizer.h"
|
||||
#include "renderer/gfx-base/GFXDef-common.h"
|
||||
|
||||
namespace cc {
|
||||
namespace {
|
||||
gfx::AttributeList defAttrs = {
|
||||
gfx::Attribute{gfx::ATTR_NAME_POSITION, gfx::Format::RGB32F},
|
||||
gfx::Attribute{gfx::ATTR_NAME_NORMAL, gfx::Format::RGB32F},
|
||||
gfx::Attribute{gfx::ATTR_NAME_TEX_COORD, gfx::Format::RG32F},
|
||||
gfx::Attribute{gfx::ATTR_NAME_TANGENT, gfx::Format::RGBA32F},
|
||||
gfx::Attribute{gfx::ATTR_NAME_COLOR, gfx::Format::RGBA32F},
|
||||
};
|
||||
} // namespace
|
||||
|
||||
Mesh *MeshUtils::createMesh(const IGeometry &geometry, Mesh *out /*= nullptr*/, const ICreateMeshOptions &options /*= {}*/) {
|
||||
if (!out) {
|
||||
out = ccnew Mesh();
|
||||
}
|
||||
|
||||
out->reset(createMeshInfo(geometry, options));
|
||||
return out;
|
||||
}
|
||||
|
||||
Mesh::ICreateInfo MeshUtils::createMeshInfo(const IGeometry &geometry, const ICreateMeshOptions &options /* = {}*/) {
|
||||
// Collect attributes and calculate length of result vertex buffer.
|
||||
gfx::AttributeList attributes;
|
||||
uint32_t stride = 0;
|
||||
struct Channel {
|
||||
uint32_t offset{0};
|
||||
ccstd::vector<float> data; // float?
|
||||
gfx::Attribute attribute;
|
||||
};
|
||||
ccstd::vector<Channel> channels;
|
||||
uint32_t vertCount = 0;
|
||||
|
||||
const gfx::Attribute *attr = nullptr;
|
||||
|
||||
ccstd::vector<float> positions(geometry.positions);
|
||||
|
||||
if (!positions.empty()) {
|
||||
attr = nullptr;
|
||||
if (geometry.attributes.has_value()) {
|
||||
for (const auto &att : geometry.attributes.value()) {
|
||||
if (att.name == gfx::ATTR_NAME_POSITION) {
|
||||
attr = &att;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (attr == nullptr) {
|
||||
attr = &defAttrs[0];
|
||||
}
|
||||
|
||||
attributes.emplace_back(*attr);
|
||||
const auto &info = gfx::GFX_FORMAT_INFOS[static_cast<uint32_t>(attr->format)];
|
||||
vertCount = std::max(vertCount, static_cast<uint32_t>(std::floor(positions.size() / info.count)));
|
||||
channels.emplace_back(Channel{stride, positions, *attr});
|
||||
stride += info.size;
|
||||
}
|
||||
|
||||
if (geometry.normals.has_value() && !geometry.normals.value().empty()) {
|
||||
attr = nullptr;
|
||||
if (geometry.attributes.has_value()) {
|
||||
for (const auto &att : geometry.attributes.value()) {
|
||||
if (att.name == gfx::ATTR_NAME_NORMAL) {
|
||||
attr = &att;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (attr == nullptr) {
|
||||
attr = &defAttrs[1];
|
||||
}
|
||||
|
||||
attributes.emplace_back(*attr);
|
||||
const auto &info = gfx::GFX_FORMAT_INFOS[static_cast<uint32_t>(attr->format)];
|
||||
vertCount = std::max(vertCount, static_cast<uint32_t>(std::floor(geometry.normals->size() / info.count)));
|
||||
channels.emplace_back(Channel{stride, geometry.normals.value(), *attr});
|
||||
stride += info.size;
|
||||
}
|
||||
|
||||
if (geometry.uvs.has_value() && !geometry.uvs.value().empty()) {
|
||||
attr = nullptr;
|
||||
if (geometry.attributes.has_value()) {
|
||||
for (const auto &att : geometry.attributes.value()) {
|
||||
if (att.name == gfx::ATTR_NAME_TEX_COORD) {
|
||||
attr = &att;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (attr == nullptr) {
|
||||
attr = &defAttrs[2];
|
||||
}
|
||||
|
||||
attributes.emplace_back(*attr);
|
||||
const auto &info = gfx::GFX_FORMAT_INFOS[static_cast<uint32_t>(attr->format)];
|
||||
vertCount = std::max(vertCount, static_cast<uint32_t>(std::floor(geometry.uvs->size() / info.count)));
|
||||
channels.emplace_back(Channel{stride, geometry.uvs.value(), *attr});
|
||||
stride += info.size;
|
||||
}
|
||||
|
||||
if (geometry.tangents.has_value() && !geometry.tangents.value().empty()) {
|
||||
attr = nullptr;
|
||||
if (geometry.attributes.has_value()) {
|
||||
for (const auto &att : geometry.attributes.value()) {
|
||||
if (att.name == gfx::ATTR_NAME_TANGENT) {
|
||||
attr = &att;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (attr == nullptr) {
|
||||
attr = &defAttrs[3];
|
||||
}
|
||||
|
||||
attributes.emplace_back(*attr);
|
||||
const auto &info = gfx::GFX_FORMAT_INFOS[static_cast<uint32_t>(attr->format)];
|
||||
vertCount = std::max(vertCount, static_cast<uint32_t>(std::floor(geometry.tangents->size() / info.count)));
|
||||
channels.emplace_back(Channel{stride, geometry.tangents.value(), *attr});
|
||||
stride += info.size;
|
||||
}
|
||||
|
||||
if (geometry.colors.has_value() && !geometry.colors.value().empty()) {
|
||||
attr = nullptr;
|
||||
if (geometry.attributes.has_value()) {
|
||||
for (const auto &att : geometry.attributes.value()) {
|
||||
if (att.name == gfx::ATTR_NAME_COLOR) {
|
||||
attr = &att;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (attr == nullptr) {
|
||||
attr = &defAttrs[4];
|
||||
}
|
||||
|
||||
attributes.emplace_back(*attr);
|
||||
const auto &info = gfx::GFX_FORMAT_INFOS[static_cast<uint32_t>(attr->format)];
|
||||
vertCount = std::max(vertCount, static_cast<uint32_t>(std::floor(geometry.colors->size() / info.count)));
|
||||
channels.emplace_back(Channel{stride, geometry.colors.value(), *attr});
|
||||
stride += info.size;
|
||||
}
|
||||
|
||||
if (geometry.customAttributes.has_value()) {
|
||||
for (const auto &ca : geometry.customAttributes.value()) {
|
||||
const auto &info = gfx::GFX_FORMAT_INFOS[static_cast<uint32_t>(attr->format)];
|
||||
attributes.emplace_back(ca.attr);
|
||||
vertCount = std::max(vertCount, static_cast<uint32_t>(std::floor(ca.values.size() / info.count)));
|
||||
channels.emplace_back(Channel{stride, ca.values, ca.attr});
|
||||
stride += info.size;
|
||||
}
|
||||
}
|
||||
|
||||
// Use this to generate final merged buffer.
|
||||
BufferBlob bufferBlob;
|
||||
|
||||
// Fill vertex buffer.
|
||||
auto *vertexBuffer = ccnew ArrayBuffer(vertCount * stride);
|
||||
DataView vertexBufferView(vertexBuffer);
|
||||
for (const auto &channel : channels) {
|
||||
writeBuffer(vertexBufferView, channel.data, channel.attribute.format, channel.offset, stride);
|
||||
}
|
||||
bufferBlob.setNextAlignment(0);
|
||||
Mesh::IVertexBundle vertexBundle;
|
||||
Mesh::IBufferView buffferView;
|
||||
|
||||
buffferView.offset = bufferBlob.getLength();
|
||||
buffferView.length = static_cast<uint32_t>(vertexBuffer->byteLength());
|
||||
buffferView.count = vertCount;
|
||||
buffferView.stride = stride;
|
||||
vertexBundle.attributes = attributes;
|
||||
vertexBundle.view = buffferView;
|
||||
|
||||
bufferBlob.addBuffer(vertexBuffer);
|
||||
|
||||
// Fill index buffer.
|
||||
ArrayBuffer::Ptr indexBuffer;
|
||||
uint32_t idxCount = 0;
|
||||
const uint32_t idxStride = 2;
|
||||
if (geometry.indices.has_value()) {
|
||||
const ccstd::vector<uint32_t> &indices = geometry.indices.value();
|
||||
idxCount = static_cast<uint32_t>(indices.size());
|
||||
indexBuffer = ccnew ArrayBuffer(idxStride * idxCount);
|
||||
DataView indexBufferView(indexBuffer);
|
||||
writeBuffer(indexBufferView, indices, gfx::Format::R16UI);
|
||||
}
|
||||
|
||||
// Create primitive.
|
||||
Mesh::ISubMesh primitive;
|
||||
primitive.vertexBundelIndices = {0};
|
||||
primitive.primitiveMode = geometry.primitiveMode.has_value() ? geometry.primitiveMode.value() : gfx::PrimitiveMode::TRIANGLE_LIST;
|
||||
|
||||
if (indexBuffer) {
|
||||
bufferBlob.setNextAlignment(idxStride);
|
||||
Mesh::IBufferView bufferView;
|
||||
bufferView.offset = bufferBlob.getLength();
|
||||
bufferView.length = indexBuffer->byteLength();
|
||||
bufferView.count = idxCount;
|
||||
bufferView.stride = idxStride;
|
||||
primitive.indexView = bufferView;
|
||||
bufferBlob.addBuffer(indexBuffer);
|
||||
}
|
||||
|
||||
ccstd::optional<Vec3> minPosition = geometry.minPos;
|
||||
if (!minPosition.has_value() && options.calculateBounds.has_value() && options.calculateBounds.value()) {
|
||||
minPosition = Vec3(std::numeric_limits<float>::infinity(), std::numeric_limits<float>::infinity(), std::numeric_limits<float>::infinity());
|
||||
for (uint32_t iVertex = 0; iVertex < vertCount; ++iVertex) {
|
||||
Vec3::min(minPosition.value(), Vec3(positions[iVertex * 3 + 0], positions[iVertex * 3 + 1], positions[iVertex * 3 + 2]), &minPosition.value());
|
||||
}
|
||||
}
|
||||
|
||||
ccstd::optional<Vec3> maxPosition = geometry.maxPos;
|
||||
if (!maxPosition.has_value() && options.calculateBounds.has_value() && options.calculateBounds.value()) {
|
||||
maxPosition = Vec3(-std::numeric_limits<float>::infinity(), -std::numeric_limits<float>::infinity(), -std::numeric_limits<float>::infinity());
|
||||
for (uint32_t iVertex = 0; iVertex < vertCount; ++iVertex) {
|
||||
Vec3::max(maxPosition.value(), Vec3(positions[iVertex * 3 + 0], positions[iVertex * 3 + 1], positions[iVertex * 3 + 2]), &maxPosition.value());
|
||||
}
|
||||
}
|
||||
|
||||
// Create mesh struct
|
||||
Mesh::IStruct meshStruct;
|
||||
meshStruct.vertexBundles = {vertexBundle};
|
||||
meshStruct.primitives = {primitive};
|
||||
|
||||
if (minPosition.has_value()) {
|
||||
meshStruct.minPosition = minPosition.value();
|
||||
}
|
||||
if (maxPosition.has_value()) {
|
||||
meshStruct.maxPosition = maxPosition.value();
|
||||
}
|
||||
|
||||
Mesh::ICreateInfo createInfo;
|
||||
createInfo.structInfo = std::move(meshStruct);
|
||||
createInfo.data = Uint8Array(bufferBlob.getCombined());
|
||||
return createInfo;
|
||||
}
|
||||
|
||||
static inline uint32_t getPadding(uint32_t length, uint32_t align) {
|
||||
if (align > 0U) {
|
||||
const uint32_t remainder = length % align;
|
||||
if (remainder != 0U) {
|
||||
const uint32_t padding = align - remainder;
|
||||
return padding;
|
||||
}
|
||||
}
|
||||
|
||||
return 0U;
|
||||
}
|
||||
|
||||
Mesh *MeshUtils::createDynamicMesh(index_t primitiveIndex, const IDynamicGeometry &geometry, Mesh *out /*= nullptr*/, const ICreateDynamicMeshOptions &options /*= {}*/) {
|
||||
if (!out) {
|
||||
out = ccnew Mesh();
|
||||
}
|
||||
|
||||
out->reset(MeshUtils::createDynamicMeshInfo(geometry, options));
|
||||
out->initialize();
|
||||
out->updateSubMesh(primitiveIndex, geometry);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
Mesh::ICreateInfo MeshUtils::createDynamicMeshInfo(const IDynamicGeometry &geometry, const ICreateDynamicMeshOptions &options /* = {}*/) {
|
||||
gfx::AttributeList attributes;
|
||||
uint32_t stream = 0U;
|
||||
|
||||
if (!geometry.positions.empty()) {
|
||||
attributes.push_back({gfx::ATTR_NAME_POSITION, gfx::Format::RGB32F, false, stream++, false, 0U});
|
||||
}
|
||||
|
||||
if (geometry.normals.has_value() && !geometry.normals.value().empty()) {
|
||||
attributes.push_back({gfx::ATTR_NAME_NORMAL, gfx::Format::RGB32F, false, stream++, false, 0U});
|
||||
}
|
||||
|
||||
if (geometry.uvs.has_value() && !geometry.uvs.value().empty()) {
|
||||
attributes.push_back({gfx::ATTR_NAME_TEX_COORD, gfx::Format::RG32F, false, stream++, false, 0U});
|
||||
}
|
||||
|
||||
if (geometry.tangents.has_value() && !geometry.tangents.value().empty()) {
|
||||
attributes.push_back({gfx::ATTR_NAME_TANGENT, gfx::Format::RGBA32F, false, stream++, false, 0U});
|
||||
}
|
||||
|
||||
if (geometry.colors.has_value() && !geometry.colors.value().empty()) {
|
||||
attributes.push_back({gfx::ATTR_NAME_COLOR, gfx::Format::RGBA32F, false, stream++, false, 0U});
|
||||
}
|
||||
|
||||
if (geometry.customAttributes.has_value()) {
|
||||
for (const auto &ca : geometry.customAttributes.value()) {
|
||||
auto attr = ca.attr;
|
||||
attr.stream = stream++;
|
||||
attributes.emplace_back(attr);
|
||||
}
|
||||
}
|
||||
|
||||
ccstd::vector<Mesh::IVertexBundle> vertexBundles;
|
||||
ccstd::vector<Mesh::ISubMesh> primitives;
|
||||
uint32_t dataSize = 0U;
|
||||
|
||||
for (auto i = 0U; i < options.maxSubMeshes; i++) {
|
||||
Mesh::ISubMesh primitive;
|
||||
primitive.primitiveMode = geometry.primitiveMode.has_value() ? geometry.primitiveMode.value() : gfx::PrimitiveMode::TRIANGLE_LIST;
|
||||
|
||||
// add vertex buffers
|
||||
for (const auto &attr : attributes) {
|
||||
const auto &formatInfo = gfx::GFX_FORMAT_INFOS[static_cast<uint32_t>(attr.format)];
|
||||
uint32_t vertexBufferSize = options.maxSubMeshVertices * formatInfo.size;
|
||||
|
||||
Mesh::IBufferView vertexView = {
|
||||
dataSize,
|
||||
vertexBufferSize,
|
||||
0U,
|
||||
formatInfo.size};
|
||||
|
||||
Mesh::IVertexBundle vertexBundle = {
|
||||
0U,
|
||||
vertexView,
|
||||
{attr}};
|
||||
|
||||
const auto vertexBundleIndex = static_cast<uint32_t>(vertexBundles.size());
|
||||
primitive.vertexBundelIndices.emplace_back(vertexBundleIndex);
|
||||
vertexBundles.emplace_back(vertexBundle);
|
||||
dataSize += vertexBufferSize;
|
||||
}
|
||||
|
||||
// add index buffer
|
||||
uint32_t stride = 0U;
|
||||
if (geometry.indices16.has_value() && !geometry.indices16.value().empty()) {
|
||||
stride = sizeof(uint16_t);
|
||||
} else if (geometry.indices32.has_value() && !geometry.indices32.value().empty()) {
|
||||
stride = sizeof(uint32_t);
|
||||
}
|
||||
|
||||
if (stride > 0U) {
|
||||
dataSize += getPadding(dataSize, stride);
|
||||
uint32_t indexBufferSize = options.maxSubMeshIndices * stride;
|
||||
|
||||
Mesh::IBufferView indexView = {
|
||||
dataSize,
|
||||
indexBufferSize,
|
||||
0U,
|
||||
stride};
|
||||
|
||||
primitive.indexView = indexView;
|
||||
dataSize += indexBufferSize;
|
||||
}
|
||||
|
||||
primitives.emplace_back(primitive);
|
||||
}
|
||||
|
||||
Mesh::IDynamicInfo dynamicInfo = {options.maxSubMeshes,
|
||||
options.maxSubMeshVertices,
|
||||
options.maxSubMeshIndices};
|
||||
|
||||
Mesh::IDynamicStruct dynamicStruct;
|
||||
dynamicStruct.info = dynamicInfo;
|
||||
dynamicStruct.bounds.resize(options.maxSubMeshes);
|
||||
for (auto &bound : dynamicStruct.bounds) {
|
||||
bound.setValid(false);
|
||||
}
|
||||
|
||||
Mesh::IStruct meshStruct;
|
||||
meshStruct.vertexBundles = vertexBundles;
|
||||
meshStruct.primitives = primitives;
|
||||
meshStruct.dynamic = std::move(dynamicStruct);
|
||||
|
||||
Mesh::ICreateInfo createInfo;
|
||||
createInfo.structInfo = std::move(meshStruct);
|
||||
createInfo.data = Uint8Array(dataSize);
|
||||
return createInfo;
|
||||
}
|
||||
|
||||
void MeshUtils::inflateMesh(const Mesh::IStruct &structInfo, Uint8Array &data) {
|
||||
uLongf uncompressedSize = 0U;
|
||||
for (const auto &prim : structInfo.primitives) {
|
||||
if (prim.indexView.has_value()) {
|
||||
uncompressedSize += prim.indexView->length + prim.indexView->stride;
|
||||
}
|
||||
if (prim.cluster.has_value()) {
|
||||
uncompressedSize += prim.cluster->vertexView.length + prim.cluster->vertexView.stride;
|
||||
uncompressedSize += prim.cluster->triangleView.length + prim.cluster->triangleView.stride;
|
||||
uncompressedSize += prim.cluster->clusterView.length + prim.cluster->clusterView.stride;
|
||||
uncompressedSize += prim.cluster->coneView.length + prim.cluster->coneView.stride;
|
||||
}
|
||||
}
|
||||
for (const auto &vb : structInfo.vertexBundles) {
|
||||
uncompressedSize += vb.view.length + vb.view.stride;
|
||||
}
|
||||
auto uncompressedData = Uint8Array(static_cast<uint32_t>(uncompressedSize));
|
||||
auto res = uncompress(uncompressedData.buffer()->getData(), &uncompressedSize, data.buffer()->getData(), data.byteLength());
|
||||
data = Uint8Array(uncompressedData.buffer(), 0, static_cast<uint32_t>(uncompressedSize));
|
||||
}
|
||||
|
||||
void MeshUtils::decodeMesh(Mesh::IStruct &structInfo, Uint8Array &data) {
|
||||
BufferBlob bufferBlob;
|
||||
|
||||
for (auto &bundle : structInfo.vertexBundles) {
|
||||
auto &view = bundle.view;
|
||||
auto bound = view.count * view.stride;
|
||||
auto *buffer = ccnew ArrayBuffer(bound);
|
||||
auto vertex = Uint8Array(data.buffer(), view.offset, view.length);
|
||||
int res = meshopt_decodeVertexBuffer(buffer->getData(), view.count, view.stride, vertex.buffer()->getData() + vertex.byteOffset(), view.length);
|
||||
if (res < 0) {
|
||||
assert(false && "failed to decode vertex buffer");
|
||||
}
|
||||
|
||||
bufferBlob.setNextAlignment(view.stride);
|
||||
Mesh::IVertexBundle vertexBundle;
|
||||
Mesh::IBufferView buffferView;
|
||||
buffferView.offset = bufferBlob.getLength();
|
||||
buffferView.length = bound;
|
||||
buffferView.count = view.count;
|
||||
buffferView.stride = view.stride;
|
||||
bufferBlob.addBuffer(buffer);
|
||||
|
||||
bundle.view = buffferView;
|
||||
}
|
||||
|
||||
for (auto &primitive : structInfo.primitives) {
|
||||
if (!primitive.indexView.has_value()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto view = *primitive.indexView;
|
||||
auto bound = view.count * view.stride;
|
||||
auto *buffer = ccnew ArrayBuffer(bound);
|
||||
auto index = DataView(data.buffer(), view.offset, view.length);
|
||||
int res = meshopt_decodeIndexBuffer(buffer->getData(), view.count, view.stride, index.buffer()->getData() + index.byteOffset(), view.length);
|
||||
if (res < 0) {
|
||||
assert(false && "failed to decode index buffer");
|
||||
}
|
||||
|
||||
bufferBlob.setNextAlignment(view.stride);
|
||||
Mesh::IBufferView buffferView;
|
||||
buffferView.offset = bufferBlob.getLength();
|
||||
buffferView.length = bound;
|
||||
buffferView.count = view.count;
|
||||
buffferView.stride = view.stride;
|
||||
bufferBlob.addBuffer(buffer);
|
||||
|
||||
primitive.indexView = buffferView;
|
||||
}
|
||||
|
||||
data = Uint8Array(bufferBlob.getCombined());
|
||||
}
|
||||
|
||||
} // namespace cc
|
||||
101
cocos/3d/misc/CreateMesh.h
Normal file
101
cocos/3d/misc/CreateMesh.h
Normal file
@@ -0,0 +1,101 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2021-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/optional.h"
|
||||
|
||||
#include "3d/assets/Mesh.h"
|
||||
#include "primitive/PrimitiveDefine.h"
|
||||
|
||||
namespace cc {
|
||||
|
||||
struct ICreateMeshOptions {
|
||||
/**
|
||||
* @en calculate mesh's aabb or not
|
||||
* @zh 是否计算模型的包围盒。
|
||||
*/
|
||||
ccstd::optional<bool> calculateBounds;
|
||||
};
|
||||
|
||||
struct ICreateDynamicMeshOptions {
|
||||
/**
|
||||
* @en max submesh count
|
||||
* @zh 最大子模型个数。
|
||||
*/
|
||||
uint32_t maxSubMeshes{1U};
|
||||
|
||||
/**
|
||||
* @en max submesh vertex count
|
||||
* @zh 子模型最大顶点个数。
|
||||
*/
|
||||
uint32_t maxSubMeshVertices{1024U};
|
||||
|
||||
/**
|
||||
* @en max submesh index count
|
||||
* @zh 子模型最大索引个数。
|
||||
*/
|
||||
uint32_t maxSubMeshIndices{1024U};
|
||||
};
|
||||
|
||||
/**
|
||||
* @en mesh utility class, use to create mesh.
|
||||
* @zh 网格工具类,用于创建网格。
|
||||
*/
|
||||
class MeshUtils {
|
||||
public:
|
||||
/**
|
||||
* @en create a static mesh.
|
||||
* @zh 创建一个静态网格。
|
||||
*/
|
||||
static Mesh *createMesh(const IGeometry &geometry, Mesh *out = nullptr, const ICreateMeshOptions &options = {});
|
||||
|
||||
/**
|
||||
* @en create a static mesh ICreateInfo.
|
||||
* @zh 创建一个静态网格ICreateInfo。
|
||||
*/
|
||||
static Mesh::ICreateInfo createMeshInfo(const IGeometry &geometry, const ICreateMeshOptions &options = {});
|
||||
|
||||
/**
|
||||
* @en create a dynamic mesh.
|
||||
* @zh 创建一个动态网格。
|
||||
*/
|
||||
static Mesh *createDynamicMesh(index_t primitiveIndex, const IDynamicGeometry &geometry, Mesh *out = nullptr, const ICreateDynamicMeshOptions &options = {});
|
||||
|
||||
/**
|
||||
* @en create a dynamic mesh ICreateInfo.
|
||||
* @zh 创建一个动态网格ICreateInfo。
|
||||
*/
|
||||
static Mesh::ICreateInfo createDynamicMeshInfo(const IDynamicGeometry &geometry, const ICreateDynamicMeshOptions &options = {});
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static void inflateMesh(const Mesh::IStruct &structInfo, Uint8Array &data);
|
||||
|
||||
static void decodeMesh(Mesh::IStruct &structInfo, Uint8Array &data);
|
||||
|
||||
static void dequantizeMesh(Mesh::IStruct &structInfo, Uint8Array &data);
|
||||
};
|
||||
|
||||
} // namespace cc
|
||||
Reference in New Issue
Block a user