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

109
cocos/core/assets/Asset.cpp Normal file
View File

@@ -0,0 +1,109 @@
/****************************************************************************
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 "core/assets/Asset.h"
#include "base/DeferredReleasePool.h"
#include "base/Macros.h"
#include "core/utils/Path.h"
namespace cc {
//cjh TODO:
ccstd::string getAssetUrlWithUuid(const ccstd::string &uuid, bool isNative, const ccstd::string &nativeExt, const ccstd::string &nativeName = "") { //NOLINT
return "";
}
ccstd::string Asset::getNativeUrl() const {
if (_nativeUrl.empty()) {
if (_native.empty()) {
return "";
}
const auto &name = _native;
if (name[0] == 47) { // '/'
// remove library tag
// not imported in library, just created on-the-fly
return name.substr(1);
}
if (name[0] == 46) { // '.'
// imported in dir where json exist
const_cast<Asset *>(this)->_nativeUrl = getAssetUrlWithUuid(_uuid, true, name);
} else {
// imported in an independent dir
const_cast<Asset *>(this)->_nativeUrl = getAssetUrlWithUuid(_uuid, true, extname(name), name);
}
}
return _nativeUrl;
}
Asset::Asset() = default;
Asset::~Asset() = default;
NativeDep Asset::getNativeDep() const {
if (!_native.empty()) {
return NativeDep(true, _uuid, _native);
}
return NativeDep();
}
void Asset::setRawAsset(const ccstd::string &filename, bool inLibrary /* = true*/) {
if (inLibrary) {
_native = filename;
} else {
_native = "/" + filename; // simply use '/' to tag location where is not in the library
}
}
void Asset::addAssetRef() {
++_assetRefCount;
}
void Asset::decAssetRef(bool autoRelease /* = true*/) {
if (_assetRefCount > 0) {
--_assetRefCount;
}
if (autoRelease) {
//cjh TODO:
}
}
void Asset::initDefault(const ccstd::optional<ccstd::string> &uuid) {
if (uuid.has_value()) {
_uuid = uuid.value();
}
_isDefault = true;
}
bool Asset::destroy() {
//cjh TODO: debug(getError(12101, this._uuid));
return Super::destroy();
}
void Asset::destruct() {
CCObject::destruct();
_native.clear();
_nativeUrl.clear();
}
} // namespace cc

159
cocos/core/assets/Asset.h Normal file
View File

@@ -0,0 +1,159 @@
/****************************************************************************
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 <functional>
#include "base/std/any.h"
#include "base/std/optional.h"
#include "base/Macros.h"
#include "core/Types.h"
#include "core/data/Object.h"
#include "core/event/EventTarget.h"
namespace cc {
class Node;
class Asset : public CCObject {
public:
using Super = CCObject;
Asset();
~Asset() override;
/**
* @en
* Returns the url of this asset's native object, if none it will returns an empty string.
* @zh
* 返回该资源对应的目标平台资源的 URL如果没有将返回一个空字符串。
* @readOnly
*/
ccstd::string getNativeUrl() const;
NativeDep getNativeDep() const;
inline const ccstd::string &getUuid() const { return _uuid; }
inline void setUuid(const ccstd::string &uuid) { _uuid = uuid; }
/**
* @en
* The underlying native asset of this asset if one is available.<br>
* This property can be used to access additional details or functionality related to the asset.<br>
* This property will be initialized by the loader if `_native` is available.
* @zh
* 此资源的基础资源(如果有)。 此属性可用于访问与资源相关的其他详细信息或功能。<br>
* 如果`_native`可用,则此属性将由加载器初始化。
* @default null
* @private
*/
virtual ccstd::any getNativeAsset() const {
return _file;
}
virtual void setNativeAsset(const ccstd::any &obj) {
_file = obj;
}
/**
* @param error - null or the error info
* @param node - the created node or null
*/
using CreateNodeCallback = std::function<void(Error, Node *)>;
/**
* @en
* Create a new node using this asset in the scene.<br/>
* If this type of asset don't have its corresponding node type, this method should be null.
* @zh
* 使用该资源在场景中创建一个新节点。<br/>
* 如果这类资源没有相应的节点类型,该方法应该是空的。
*/
virtual void createNode(const CreateNodeCallback &cb) {}
void addAssetRef();
void decAssetRef(bool autoRelease = true);
inline uint32_t getAssetRefCount() const { return _assetRefCount; }
virtual void onLoaded() {}
virtual void initDefault() { initDefault(ccstd::nullopt); }
virtual void initDefault(const ccstd::optional<ccstd::string> &uuid);
virtual bool validate() const { return true; }
bool isDefault() const { return _isDefault; }
bool destroy() override;
void destruct() override;
// SERIALIZATION
/**
* @return
*/
virtual ccstd::any serialize(const ccstd::any & /*ctxForExporting*/) { return ccstd::any{}; };
/**
*
* @param data
*/
virtual void deserialize(const ccstd::any &serializedData, const ccstd::any &handle) {}
ccstd::string toString() const override { return _nativeUrl; }
protected:
/**
* @en
* Set native file name for this asset.
* @zh
* 为此资源设置原始文件名。
* @seealso nativeUrl
*
* @param filename
* @param inLibrary
* @private
*/
void setRawAsset(const ccstd::string &filename, bool inLibrary = true);
// Make _native, _nativeUrl public for deserialization
public:
ccstd::string _native;
ccstd::string _nativeUrl;
protected:
ccstd::string _uuid;
ccstd::any _file;
uint32_t _assetRefCount{0};
bool _loaded{true};
bool _isDefault{false};
CC_DISALLOW_COPY_MOVE_ASSIGN(Asset);
};
} // namespace cc

View File

@@ -0,0 +1,305 @@
/****************************************************************************
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 "renderer/gfx-base/GFXDef.h"
namespace cc {
// define a specified number for the pixel format which gfx do not have a standard definition.
#define CUSTOM_PIXEL_FORMAT (1024)
/**
* @en
* The texture pixel format, default value is RGBA8888,<br>
* you should note that textures loaded by normal image files (png, jpg) can only support RGBA8888 format,<br>
* other formats are supported by compressed file types or raw data.
* @zh
* 纹理像素格式默认值为RGBA8888<br>
* 你应该注意到普通图像文件pngjpg加载的纹理只能支持RGBA8888格式<br>
* 压缩文件类型或原始数据支持其他格式。
*/
enum class PixelFormat : uint32_t {
/**
* @en
* 16-bit pixel format containing red, green and blue channels
* @zh
* 包含 RGB 通道的 16 位纹理。
*/
RGB565 = static_cast<uint32_t>(gfx::Format::R5G6B5),
/**
* @en
* 16-bit pixel format containing red, green, blue channels with 5 bits per channel and one bit alpha channel: RGB5A1
* @zh
* 包含 RGB分别占 5 bits和 1 bit 的 alpha 通道的 16 位纹理RGB5A1。
*/
RGB5A1 = static_cast<uint32_t>(gfx::Format::RGB5A1),
/**
* @en
* 16-bit pixel format containing red, green, blue and alpha channels: RGBA4444
* @zh
* 包含 RGBA 通道的 16 位纹理RGBA4444。
*/
RGBA4444 = static_cast<uint32_t>(gfx::Format::RGBA4),
/**
* @en
* 24-bit pixel format containing red, green and blue channels: RGB888
* @zh
* 包含 RGB 通道的 24 位纹理RGB888。
*/
RGB888 = static_cast<uint32_t>(gfx::Format::RGB8),
/**
* @en
* 32-bit float pixel format containing red, green and blue channels: RGBA32F
* @zh
* 包含 RGB 通道的 32 位浮点数像素格式RGBA32F。
*/
RGB32F = static_cast<uint32_t>(gfx::Format::RGB32F),
/**
* @en
* 32-bit pixel format containing red, green, blue and alpha channels: RGBA8888
* @zh
* 包含 RGBA 四通道的 32 位整形像素格式RGBA8888。
*/
RGBA8888 = static_cast<uint32_t>(gfx::Format::RGBA8),
/**
* @en
* 32-bit float pixel format containing red, green, blue and alpha channels: RGBA32F
* @zh
* 32位浮点数像素格式RGBA32F。
*/
RGBA32F = static_cast<uint32_t>(gfx::Format::RGBA32F),
/**
* @en
* 8-bit pixel format used as masks
* @zh
* 用作蒙版的8位纹理。
*/
A8 = static_cast<uint32_t>(gfx::Format::A8),
/**
* @en
* 8-bit intensity pixel format
* @zh
* 8位强度纹理。
*/
I8 = static_cast<uint32_t>(gfx::Format::L8),
/**
* @en
* 16-bit pixel format used as masks
* @zh
* 用作蒙版的16位纹理。
*/
AI8 = static_cast<uint32_t>(gfx::Format::LA8),
/**
* @en A pixel format containing red, green, and blue channels that is PVR 2bpp compressed.
* @zh 包含 RGB 通道的 PVR 2BPP 压缩纹理格式
*/
RGB_PVRTC_2BPPV1 = static_cast<uint32_t>(gfx::Format::PVRTC_RGB2),
/**
* @en A pixel format containing red, green, blue, and alpha channels that is PVR 2bpp compressed.
* @zh 包含 RGBA 通道的 PVR 2BPP 压缩纹理格式
*/
RGBA_PVRTC_2BPPV1 = static_cast<uint32_t>(gfx::Format::PVRTC_RGBA2),
/**
* @en A pixel format containing red, green, blue, and alpha channels that is PVR 2bpp compressed.
* RGB_A_PVRTC_2BPPV1 texture is a 2x height RGB_PVRTC_2BPPV1 format texture.
* It separate the origin alpha channel to the bottom half atlas, the origin rgb channel to the top half atlas.
* @zh 包含 RGBA 通道的 PVR 2BPP 压缩纹理格式
* 这种压缩纹理格式贴图的高度是普通 RGB_PVRTC_2BPPV1 贴图高度的两倍,使用上半部分作为原始 RGB 通道数据,下半部分用来存储透明通道数据。
*/
RGB_A_PVRTC_2BPPV1 = CUSTOM_PIXEL_FORMAT,
/**
* @en A pixel format containing red, green, and blue channels that is PVR 4bpp compressed.
* @zh 包含 RGB 通道的 PVR 4BPP 压缩纹理格式
*/
RGB_PVRTC_4BPPV1 = static_cast<uint32_t>(gfx::Format::PVRTC_RGB4),
/**
* @en A pixel format containing red, green, blue and alpha channels that is PVR 4bpp compressed.
* @zh 包含 RGBA 通道的 PVR 4BPP 压缩纹理格式
*/
RGBA_PVRTC_4BPPV1 = static_cast<uint32_t>(gfx::Format::PVRTC_RGBA4),
/**
* @en A pixel format containing red, green, blue, and alpha channels that is PVR 4bpp compressed.
* RGB_A_PVRTC_4BPPV1 texture is a 2x height RGB_PVRTC_4BPPV1 format texture.
* It separate the origin alpha channel to the bottom half atlas, the origin rgb channel to the top half atlas.
* @zh 包含 RGBA 通道的 PVR 4BPP 压缩纹理格式
* 这种压缩纹理格式贴图的高度是普通 RGB_PVRTC_4BPPV1 贴图高度的两倍,使用上半部分作为原始 RGB 通道数据,下半部分用来存储透明通道数据。
*/
RGB_A_PVRTC_4BPPV1 = CUSTOM_PIXEL_FORMAT + 1,
/**
* @en A pixel format containing red, green, and blue channels that is ETC1 compressed.
* @zh 包含 RGB 通道的 ETC1 压缩纹理格式
*/
RGB_ETC1 = static_cast<uint32_t>(gfx::Format::ETC_RGB8),
/**
* @en A pixel format containing red, green, blue, and alpha channels that is ETC1 compressed.
* @zh 包含 RGBA 通道的 ETC1 压缩纹理格式
*/
RGBA_ETC1 = CUSTOM_PIXEL_FORMAT + 2,
/**
* @en A pixel format containing red, green, and blue channels that is ETC2 compressed.
* @zh 包含 RGB 通道的 ETC2 压缩纹理格式
*/
RGB_ETC2 = static_cast<uint32_t>(gfx::Format::ETC2_RGB8),
/**
* @en A pixel format containing red, green, blue, and alpha channels that is ETC2 compressed.
* @zh 包含 RGBA 通道的 ETC2 压缩纹理格式
*/
RGBA_ETC2 = static_cast<uint32_t>(gfx::Format::ETC2_RGBA8),
/**
* @en A pixel format containing red, green, blue, and alpha channels that is ASTC compressed with 4x4 block size.
* @zh 包含 RGBA 通道的 ASTC 压缩纹理格式,压缩分块大小为 4x4
*/
RGBA_ASTC_4X4 = static_cast<uint32_t>(gfx::Format::ASTC_RGBA_4X4),
/**
* @en A pixel format containing red, green, blue, and alpha channels that is ASTC compressed with 5x4 block size.
* @zh 包含 RGBA 通道的 ASTC 压缩纹理格式,压缩分块大小为 5x4
*/
RGBA_ASTC_5X4 = static_cast<uint32_t>(gfx::Format::ASTC_RGBA_5X4),
/**
* @en A pixel format containing red, green, blue, and alpha channels that is ASTC compressed with 5x5 block size.
* @zh 包含 RGBA 通道的 ASTC 压缩纹理格式,压缩分块大小为 5x5
*/
RGBA_ASTC_5X5 = static_cast<uint32_t>(gfx::Format::ASTC_RGBA_5X5),
/**
* @en A pixel format containing red, green, blue, and alpha channels that is ASTC compressed with 6x5 block size.
* @zh 包含 RGBA 通道的 ASTC 压缩纹理格式,压缩分块大小为 6x5
*/
RGBA_ASTC_6X5 = static_cast<uint32_t>(gfx::Format::ASTC_RGBA_6X5),
/**
* @en A pixel format containing red, green, blue, and alpha channels that is ASTC compressed with 6x6 block size.
* @zh 包含 RGBA 通道的 ASTC 压缩纹理格式,压缩分块大小为 6x6
*/
RGBA_ASTC_6X6 = static_cast<uint32_t>(gfx::Format::ASTC_RGBA_6X6),
/**
* @en A pixel format containing red, green, blue, and alpha channels that is ASTC compressed with 8x5 block size.
* @zh 包含 RGBA 通道的 ASTC 压缩纹理格式,压缩分块大小为 8x5
*/
RGBA_ASTC_8X5 = static_cast<uint32_t>(gfx::Format::ASTC_RGBA_8X5),
/**
* @en A pixel format containing red, green, blue, and alpha channels that is ASTC compressed with 8x6 block size.
* @zh 包含 RGBA 通道的 ASTC 压缩纹理格式,压缩分块大小为 8x6
*/
RGBA_ASTC_8X6 = static_cast<uint32_t>(gfx::Format::ASTC_RGBA_8X6),
/**
* @en A pixel format containing red, green, blue, and alpha channels that is ASTC compressed with 8x8 block size.
* @zh 包含 RGBA 通道的 ASTC 压缩纹理格式,压缩分块大小为 8x8
*/
RGBA_ASTC_8X8 = static_cast<uint32_t>(gfx::Format::ASTC_RGBA_8X8),
/**
* @en A pixel format containing red, green, blue, and alpha channels that is ASTC compressed with 10x5 block size.
* @zh 包含 RGBA 通道的 ASTC 压缩纹理格式,压缩分块大小为 10x5
*/
RGBA_ASTC_10X5 = static_cast<uint32_t>(gfx::Format::ASTC_RGBA_10X5),
/**
* @en A pixel format containing red, green, blue, and alpha channels that is ASTC compressed with 10x6 block size.
* @zh 包含 RGBA 通道的 ASTC 压缩纹理格式,压缩分块大小为 10x6
*/
RGBA_ASTC_10X6 = static_cast<uint32_t>(gfx::Format::ASTC_RGBA_10X6),
/**
* @en A pixel format containing red, green, blue, and alpha channels that is ASTC compressed with 10x8 block size.
* @zh 包含 RGBA 通道的 ASTC 压缩纹理格式,压缩分块大小为 10x8
*/
RGBA_ASTC_10X8 = static_cast<uint32_t>(gfx::Format::ASTC_RGBA_10X8),
/**
* @en A pixel format containing red, green, blue, and alpha channels that is ASTC compressed with 10x10 block size.
* @zh 包含 RGBA 通道的 ASTC 压缩纹理格式,压缩分块大小为 10x10
*/
RGBA_ASTC_10X10 = static_cast<uint32_t>(gfx::Format::ASTC_RGBA_10X10),
/**
* @en A pixel format containing red, green, blue, and alpha channels that is ASTC compressed with 12x10 block size.
* @zh 包含 RGBA 通道的 ASTC 压缩纹理格式,压缩分块大小为 12x10
*/
RGBA_ASTC_12X10 = static_cast<uint32_t>(gfx::Format::ASTC_RGBA_12X10),
/**
* @en A pixel format containing red, green, blue, and alpha channels that is ASTC compressed with 12x12 block size.
* @zh 包含 RGBA 通道的 ASTC 压缩纹理格式,压缩分块大小为 12x12
*/
RGBA_ASTC_12X12 = static_cast<uint32_t>(gfx::Format::ASTC_RGBA_12X12),
};
/**
* @en
* The texture wrap mode.
* @zh
* 纹理环绕方式。
*/
enum class WrapMode : uint32_t {
/**
* @en
* Specifies that the repeat warp mode will be used.
* @zh
* 指定环绕模式:重复纹理图像。
*/
REPEAT = static_cast<uint32_t>(gfx::Address::WRAP),
/**
* @en
* Specifies that the clamp to edge warp mode will be used.
* @zh
* 指定环绕模式:纹理边缘拉伸效果。
*/
CLAMP_TO_EDGE = static_cast<uint32_t>(gfx::Address::CLAMP),
/**
* @en
* Specifies that the mirrored repeat warp mode will be used.
* @zh
* 指定环绕模式:以镜像模式重复纹理图像。
*/
MIRRORED_REPEAT = static_cast<uint32_t>(gfx::Address::MIRROR),
/**
* @en
* Specifies that the clamp to border wrap mode will be used.
* @zh
* 指定环绕模式:超出纹理坐标部分以用户指定颜色填充。
*/
CLAMP_TO_BORDER = static_cast<uint32_t>(gfx::Address::BORDER),
};
/**
* @en
* The texture filter mode
* @zh
* 纹理过滤模式。
*/
enum class Filter : uint32_t {
NONE = static_cast<uint32_t>(gfx::Filter::NONE),
/**
* @en
* Specifies linear filtering.
* @zh
* 线性过滤模式。
*/
LINEAR = static_cast<uint32_t>(gfx::Filter::LINEAR),
/**
* @en
* Specifies nearest filtering.
* @zh
* 临近过滤模式。
*/
NEAREST = static_cast<uint32_t>(gfx::Filter::POINT),
};
} // namespace cc

View File

@@ -0,0 +1,39 @@
/****************************************************************************
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 "core/assets/Asset.h"
#include "core/assets/BufferAsset.h"
#include "core/assets/EffectAsset.h"
#include "core/assets/ImageAsset.h"
//#include "core/assets/JsonAsset.h"
#include "core/assets/Material.h"
//#include "core/assets/Prefab.h"
#include "core/assets/RenderTexture.h"
#include "core/assets/RenderingSubMesh.h"
#include "core/assets/SceneAsset.h"
#include "core/assets/TextAsset.h"
#include "core/assets/Texture2D.h"
#include "core/assets/TextureCube.h"

View File

@@ -0,0 +1,205 @@
/****************************************************************************
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 "BitmapFont.h"
#include "base/Log.h"
#include "gfx-base/GFXDevice.h"
#include "platform/Image.h"
#include "tinyxml2/tinyxml2.h"
namespace cc {
/**
* BitmapFontFace
*/
BitmapFontFace::BitmapFontFace(Font *font)
: FontFace(font) {
}
void BitmapFontFace::doInit(const FontFaceInfo & /*info*/) {
const auto &fontPath = _font->getPath();
const auto &fontData = _font->getData();
if (fontData.empty()) {
CC_LOG_ERROR("BitmapFontFace doInit failed: empty font data.");
return;
}
tinyxml2::XMLDocument doc;
auto error = doc.Parse(reinterpret_cast<const char *>(fontData.data()), fontData.size());
if (error) {
CC_LOG_ERROR("BitmapFontFace parse failed.");
return;
}
auto *fontNode = doc.RootElement();
auto *infoNode = fontNode->FirstChildElement("info");
_fontSize = infoNode->UnsignedAttribute("size");
auto *commonNode = fontNode->FirstChildElement("common");
_lineHeight = commonNode->UnsignedAttribute("lineHeight");
_textureWidth = commonNode->UnsignedAttribute("scaleW");
_textureHeight = commonNode->UnsignedAttribute("scaleH");
uint32_t pages = commonNode->UnsignedAttribute("pages");
int base = commonNode->IntAttribute("base");
// load glyphs
auto *charsNode = fontNode->FirstChildElement("chars");
auto *charNode = charsNode->FirstChildElement("char");
while (charNode) {
FontGlyph glyph;
uint32_t value{0U};
uint32_t code = charNode->UnsignedAttribute("id");
glyph.x = static_cast<int16_t>(charNode->IntAttribute("x"));
glyph.y = static_cast<int16_t>(charNode->IntAttribute("y"));
glyph.width = static_cast<uint16_t>(charNode->UnsignedAttribute("width"));
glyph.height = static_cast<uint16_t>(charNode->UnsignedAttribute("height"));
glyph.bearingX = static_cast<int16_t>(charNode->IntAttribute("xoffset"));
glyph.bearingY = static_cast<int16_t>(base - charNode->IntAttribute("yoffset"));
glyph.advance = static_cast<int16_t>(charNode->IntAttribute("xadvance"));
glyph.page = charNode->UnsignedAttribute("page");
_glyphs[code] = glyph;
charNode = charNode->NextSiblingElement();
}
// load textures
auto *pagesNode = fontNode->FirstChildElement("pages");
auto *pageNode = pagesNode->FirstChildElement("page");
_textures.resize(pages, nullptr);
ccstd::string path = fontPath;
auto pos = fontPath.rfind('/');
if (pos == ccstd::string::npos) {
pos = fontPath.rfind('\\');
}
if (pos != ccstd::string::npos) {
path = fontPath.substr(0, pos + 1);
}
while (pageNode) {
uint32_t id = pageNode->UnsignedAttribute("id");
ccstd::string file = pageNode->Attribute("file");
_textures[id] = loadTexture(path + file);
pageNode = pageNode->NextSiblingElement();
}
// load kernings
auto *kerningsNode = fontNode->FirstChildElement("kernings");
auto *kerningNode = kerningsNode->FirstChildElement("kerning");
while (kerningNode) {
KerningPair pair;
pair.prevCode = kerningNode->UnsignedAttribute("first");
pair.nextCode = kerningNode->UnsignedAttribute("second");
_kernings[pair] = static_cast<float>(kerningNode->IntAttribute("amount"));
kerningNode = kerningNode->NextSiblingElement();
}
}
const FontGlyph *BitmapFontFace::getGlyph(uint32_t code) {
auto iter = _glyphs.find(code);
if (iter != _glyphs.end()) {
return &iter->second;
}
CC_LOG_WARNING("BitmapFontFace getGlyph failed, character: %u.", code);
return nullptr;
}
float BitmapFontFace::getKerning(uint32_t prevCode, uint32_t nextCode) {
if (_kernings.empty()) {
return 0.0F;
}
const auto &iter = _kernings.find({prevCode, nextCode});
if (iter != _kernings.end()) {
return iter->second;
}
return 0.0F;
}
gfx::Texture *BitmapFontFace::loadTexture(const ccstd::string &path) {
auto *image = ccnew Image();
bool success = image->initWithImageFile(path);
if (!success) {
CC_LOG_WARNING("BitmapFontFace initWithImageFile failed, path: %s.", path.c_str());
delete image;
return nullptr;
}
if (image->getRenderFormat() != gfx::Format::R8 && image->getRenderFormat() != gfx::Format::L8) {
CC_LOG_WARNING("BitmapFontFace loadTexture with invalid format, path: %s.", path.c_str());
delete image;
return nullptr;
}
auto width = static_cast<uint32_t>(image->getWidth());
auto height = static_cast<uint32_t>(image->getHeight());
auto *device = gfx::Device::getInstance();
auto *texture = device->createTexture({gfx::TextureType::TEX2D,
gfx::TextureUsageBit::SAMPLED | gfx::TextureUsageBit::TRANSFER_DST,
gfx::Format::R8,
width,
height});
gfx::BufferDataList buffers{image->getData()};
gfx::BufferTextureCopyList regions = {{0U,
0U,
0U,
{0U, 0U, 0U},
{width, height, 1U},
{0U, 0U, 1U}}};
device->copyBuffersToTexture(buffers, texture, regions);
delete image;
return texture;
}
/**
* BitmapFont
*/
BitmapFont::BitmapFont(const ccstd::string &path)
: Font(FontType::BITMAP, path) {
}
FontFace *BitmapFont::createFace(const FontFaceInfo &info) {
auto *face = ccnew BitmapFontFace(this);
face->doInit(info);
uint32_t fontSize = face->getFontSize();
_faces[fontSize] = face;
return face;
}
} // namespace cc

View File

@@ -0,0 +1,67 @@
/****************************************************************************
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 "Font.h"
namespace cc {
/**
* BitmapFontFace
*/
class BitmapFontFace : public FontFace {
public:
explicit BitmapFontFace(Font *font);
~BitmapFontFace() override = default;
BitmapFontFace(const BitmapFontFace &) = delete;
BitmapFontFace(BitmapFontFace &&) = delete;
BitmapFontFace &operator=(const BitmapFontFace &) = delete;
BitmapFontFace &operator=(BitmapFontFace &&) = delete;
const FontGlyph *getGlyph(uint32_t code) override;
float getKerning(uint32_t prevCode, uint32_t nextCode) override;
private:
void doInit(const FontFaceInfo &info) override;
static gfx::Texture *loadTexture(const ccstd::string &path);
friend class BitmapFont;
};
/**
* BitmapFont
*/
class BitmapFont : public Font {
public:
explicit BitmapFont(const ccstd::string &path);
~BitmapFont() override = default;
BitmapFont(const BitmapFont &) = delete;
BitmapFont(BitmapFont &&) = delete;
BitmapFont &operator=(const BitmapFont &) = delete;
BitmapFont &operator=(BitmapFont &&) = delete;
FontFace *createFace(const FontFaceInfo &info) override;
};
} // namespace cc

View File

@@ -0,0 +1,37 @@
/****************************************************************************
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 "core/assets/BufferAsset.h"
namespace cc {
ccstd::any BufferAsset::getNativeAsset() const {
return _buffer;
}
void BufferAsset::setNativeAsset(const ccstd::any &obj) {
_buffer = ccstd::any_cast<ArrayBuffer *>(obj);
}
} // namespace cc

View File

@@ -0,0 +1,52 @@
/****************************************************************************
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 "core/ArrayBuffer.h"
#include "core/assets/Asset.h"
namespace cc {
class BufferAsset final : public Asset {
public:
BufferAsset() = default;
~BufferAsset() override = default;
inline ArrayBuffer *getBuffer() const { return _buffer; }
inline void setNativeAssetForJS(ArrayBuffer *buffer) { _buffer = buffer; }
inline ArrayBuffer *getNativeAssetForJS() const { return _buffer; }
ccstd::any getNativeAsset() const override;
void setNativeAsset(const ccstd::any &obj) override;
bool validate() const override { return _buffer != nullptr; }
private:
ArrayBuffer::Ptr _buffer;
CC_DISALLOW_COPY_MOVE_ASSIGN(BufferAsset);
};
} // namespace cc

View File

@@ -0,0 +1,354 @@
/****************************************************************************
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 "core/assets/EffectAsset.h"
#include "ProgramUtils.h"
#include "cocos.h"
#include "cocos/renderer/pipeline/custom/RenderingModule.h"
#include "core/Root.h"
#include "core/platform/Debug.h"
#include "engine/BaseEngine.h"
#include "renderer/core/ProgramLib.h"
namespace cc {
IPassStates::IPassStates(const IPassInfoFull &o) {
*this = o;
}
IPassStates &IPassStates::operator=(const IPassInfoFull &o) {
priority = o.priority;
primitive = o.primitive;
stage = o.stage;
rasterizerState = o.rasterizerState;
depthStencilState = o.depthStencilState;
blendState = o.blendState;
dynamicStates = o.dynamicStates;
phase = o.phase;
subpass = o.subpass;
return *this;
}
void IPassStates::overrides(const IPassInfoFull &o) {
if (o.priority.has_value()) {
this->priority = o.priority.value();
}
if (o.primitive.has_value()) {
this->primitive = o.primitive.value();
}
if (o.stage.has_value()) {
this->stage = o.stage.value();
}
if (o.rasterizerState.has_value()) {
this->rasterizerState = o.rasterizerState.value();
}
if (o.depthStencilState.has_value()) {
this->depthStencilState = o.depthStencilState.value();
}
if (o.blendState.has_value()) {
this->blendState = o.blendState.value();
}
if (o.dynamicStates.has_value()) {
this->dynamicStates = o.dynamicStates.value();
}
if (o.phase.has_value()) {
this->phase = o.phase.value();
}
if (o.subpass.has_value()) {
this->subpass = o.subpass.value();
}
}
EffectAsset::RegisteredEffectAssetMap EffectAsset::effects;
bool EffectAsset::layoutValid = true;
/* static */
void EffectAsset::registerAsset(EffectAsset *asset) {
if (asset == nullptr) {
return;
}
EffectAsset::effects.emplace(asset->getName(), asset);
layoutValid = false;
}
/* static */
void EffectAsset::remove(const ccstd::string &name) {
auto iter = EffectAsset::effects.find(name);
if (iter != EffectAsset::effects.end() && iter->second->getName() == name) {
EffectAsset::effects.erase(iter);
return;
}
iter = EffectAsset::effects.begin();
for (; iter != EffectAsset::effects.end(); ++iter) {
if (iter->second->getUuid() == name) {
break;
}
}
if (iter != EffectAsset::effects.end()) {
EffectAsset::effects.erase(iter);
}
}
/* static */
void EffectAsset::remove(EffectAsset *asset) {
if (asset == nullptr) {
return;
}
auto iter = EffectAsset::effects.find(asset->getName());
if (iter != EffectAsset::effects.end() && iter->second == asset) {
EffectAsset::effects.erase(iter);
}
}
/* static */
EffectAsset *EffectAsset::get(const ccstd::string &name) {
auto iter = EffectAsset::effects.find(name);
if (iter != EffectAsset::effects.end()) {
return iter->second;
}
iter = EffectAsset::effects.begin();
for (; iter != EffectAsset::effects.end(); ++iter) {
if (iter->second->getUuid() == name) {
return iter->second;
}
}
static ccstd::vector<ccstd::string> legacyBuiltinEffectNames{
"planar-shadow",
"skybox",
"deferred-lighting",
"bloom",
"copy-pass",
"post-process",
"profiler",
"splash-screen",
"standard",
"unlit",
"sprite",
"particle",
"particle-gpu",
"particle-trail",
"billboard",
"terrain",
"graphics",
"clear-stencil",
"spine",
"occlusion-query",
"geometry-renderer",
"debug-renderer"};
for (auto &legacyName : legacyBuiltinEffectNames) {
if (name == legacyName) {
debug::warnID(16101, name);
}
}
return nullptr;
}
void EffectAsset::onLoaded() {
auto *programLib = render::getProgramLibrary();
if (programLib) {
render::addEffectDefaultProperties(*this);
programLib->addEffect(this);
} else {
ProgramLib::getInstance()->registerEffect(this);
}
EffectAsset::registerAsset(this);
#if !CC_EDITOR
if (CC_CURRENT_ENGINE()->isInited()) {
precompile();
} else {
_engineEventId = CC_CURRENT_ENGINE()->on<BaseEngine::EngineStatusChange>([this](BaseEngine * /*emitter*/, BaseEngine::EngineStatus status) {
if (status == BaseEngine::EngineStatus::ON_START) {
this->precompile();
}
});
}
#endif
}
bool EffectAsset::destroy() {
EffectAsset::remove(this);
if (CC_CURRENT_ENGINE()->isInited()) {
CC_CURRENT_ENGINE()->off(_engineEventId);
}
return Super::destroy();
}
void EffectAsset::initDefault(const ccstd::optional<ccstd::string> &uuid) {
Super::initDefault(uuid);
const auto *effect = EffectAsset::get("builtin-unlit");
_name = "builtin-unlit";
_shaders = effect->_shaders;
_combinations = effect->_combinations;
_techniques = effect->_techniques; // NOTE: it will copy effect->_techniques to _techniques and _techniques will kept by SE_HOLD_RETURN_VALUE
}
bool EffectAsset::validate() const {
return !_techniques.empty() && !_shaders.empty();
}
void EffectAsset::precompile() {
Root *root = Root::getInstance();
for (index_t i = 0; i < _shaders.size(); ++i) {
auto shader = _shaders[i];
if (i >= _combinations.size()) {
continue;
}
auto combination = _combinations[i];
if (combination.empty()) {
continue;
}
// Native Program Lib can not precompile shader variant without phaseID.
// Shaders are compiled only during the compilation of PSO. A new mechanism may be needed for pre-compilation.
auto *programLib = render::getProgramLibrary();
if (programLib == nullptr) {
ccstd::vector<MacroRecord> defines = EffectAsset::doCombine(
ccstd::vector<MacroRecord>(), combination, combination.begin());
for (auto &define: defines) {
ProgramLib::getInstance()->getGFXShader(root->getDevice(), shader.name, define,
root->getPipeline());
}
}
}
}
/*
// input
const combination = {
USE_TEXTURE: [true, false],
COLOR_MODE: [0, 1, 2, 3],
ROUGHNESS_CHANNEL: ['r', 'g', 'b'],
};
// output
const defines = [
{
USE_TEXTURE: true,
COLOR_MODE: 0,
ROUGHNESS_CHANNEL: 'r'
},
{
USE_TEXTURE: true,
COLOR_MODE: 0,
ROUGHNESS_CHANNEL: 'g'
},
{
USE_TEXTURE: true,
COLOR_MODE: 0,
ROUGHNESS_CHANNEL: 'b'
},
{
USE_TEXTURE: true,
COLOR_MODE: 1,
ROUGHNESS_CHANNEL: 'r'
},
// ... all the combinations (2x4x3 in this case)
];
*/
ccstd::vector<MacroRecord> EffectAsset::doCombine(const ccstd::vector<MacroRecord> &cur, const IPreCompileInfo &info, IPreCompileInfo::iterator iter) { // NOLINT(misc-no-recursion)
if (iter == info.end()) {
return cur;
}
const IPreCompileInfoValueType &values = iter->second;
const ccstd::string &key = iter->first;
ccstd::vector<MacroRecord> records;
if (cur.empty()) {
records = EffectAsset::generateRecords(key, values);
} else {
records = EffectAsset::insertInfoValue(cur, key, values);
}
return EffectAsset::doCombine(records, info, ++iter);
}
ccstd::vector<MacroRecord> EffectAsset::generateRecords(const ccstd::string &key, const IPreCompileInfoValueType &infoValue) {
ccstd::vector<MacroRecord> ret;
if (const auto *boolValues = ccstd::get_if<ccstd::vector<bool>>(&infoValue)) {
for (const bool value : *boolValues) {
MacroRecord record;
record[key] = value;
ret.emplace_back(record);
}
} else if (const auto *intValues = ccstd::get_if<ccstd::vector<int32_t>>(&infoValue)) {
for (const int32_t value : *intValues) {
MacroRecord record;
record[key] = value;
ret.emplace_back(record);
}
} else if (const auto *stringValues = ccstd::get_if<ccstd::vector<ccstd::string>>(&infoValue)) {
for (const ccstd::string &value : *stringValues) {
MacroRecord record;
record[key] = value;
ret.emplace_back(record);
}
} else {
CC_ABORT();
}
return ret;
}
ccstd::vector<MacroRecord> EffectAsset::insertInfoValue(const ccstd::vector<MacroRecord> &records,
const ccstd::string &key,
const IPreCompileInfoValueType &infoValue) {
ccstd::vector<MacroRecord> ret;
for (const auto &record : records) {
if (const auto *boolValues = ccstd::get_if<ccstd::vector<bool>>(&infoValue)) {
for (const bool value : *boolValues) {
MacroRecord tmpRecord = record;
tmpRecord[key] = value;
ret.emplace_back(tmpRecord);
}
} else if (const auto *intValues = ccstd::get_if<ccstd::vector<int32_t>>(&infoValue)) {
for (const int32_t value : *intValues) {
MacroRecord tmpRecord = record;
tmpRecord[key] = value;
ret.emplace_back(tmpRecord);
}
} else if (const auto *stringValues = ccstd::get_if<ccstd::vector<ccstd::string>>(&infoValue)) {
for (const ccstd::string &value : *stringValues) {
MacroRecord tmpRecord = record;
tmpRecord[key] = value;
ret.emplace_back(tmpRecord);
}
} else {
CC_ABORT();
}
}
return ret;
}
} // namespace cc

View File

@@ -0,0 +1,706 @@
/****************************************************************************
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 <tuple>
#include <type_traits>
#include "base/std/container/string.h"
#include "base/std/container/unordered_map.h"
#include "base/std/optional.h"
#include "base/Value.h"
#include "core/Types.h"
#include "core/assets/Asset.h"
#include "engine/BaseEngine.h"
#include "renderer/core/PassUtils.h"
#include "renderer/gfx-base/GFXDef.h"
#include "renderer/pipeline/Define.h"
namespace cc {
// To avoid errors when generating code using SWIG.
#if !SWIGCOCOS
// The properties in Pass are obtained from an asset file. If they are directly stored in an unordered_map,
// the order of these properties may become scrambled. To maintain their order, a vector<pair> is used
// instead. Since there is no scenario where these data objects are randomly inserted,
// only a find interface is provided.
template <typename K, typename V>
class StablePropertyMap : public ccstd::vector<std::pair<K, V>> { // NOLINT
using Super = ccstd::vector<std::pair<K, V>>;
public:
auto find(const K &key) const {
auto *self = static_cast<const Super *>(this);
return std::find_if(self->begin(), self->end(), [&](auto &ele) {
return ele.first == key;
});
}
};
#endif
template <typename K, typename V>
using UnstablePropertyContainer = ccstd::unordered_map<K, V>;
template <typename K, typename V>
using StablePropertyContainer = StablePropertyMap<K, V>;
#if CC_EDITOR
template <typename K, typename V>
using PropertyContainer = StablePropertyContainer<K, V>;
#else
template <typename K, typename V>
using PropertyContainer = UnstablePropertyContainer<K, V>;
#endif
using IPropertyHandleInfo = std::tuple<ccstd::string, uint32_t, gfx::Type>;
using IPropertyValue = ccstd::variant<ccstd::monostate, ccstd::vector<float>, ccstd::string>;
using IPropertyEditorValueType = ccstd::variant<ccstd::monostate, ccstd::string, bool, float, ccstd::vector<float>>;
using IPropertyEditorInfo = PropertyContainer<ccstd::string, IPropertyEditorValueType>;
struct IPropertyInfo {
int32_t type{0}; // auto-extracted from shader
ccstd::optional<IPropertyHandleInfo> handleInfo; // auto-generated from 'target'
ccstd::optional<ccstd::hash_t> samplerHash; // auto-generated from 'sampler'
ccstd::optional<IPropertyValue> value; // default value
ccstd::optional<bool> linear; // whether to convert the input to linear space first before applying
IPropertyEditorInfo editor; // NOTE: used only by editor.
};
struct IPassInfoFull;
struct RasterizerStateInfo {
ccstd::optional<bool> isDiscard;
ccstd::optional<bool> isFrontFaceCCW;
ccstd::optional<bool> depthBiasEnabled;
ccstd::optional<bool> isDepthClip;
ccstd::optional<bool> isMultisample;
ccstd::optional<gfx::PolygonMode> polygonMode;
ccstd::optional<gfx::ShadeModel> shadeModel;
ccstd::optional<gfx::CullMode> cullMode;
ccstd::optional<float> depthBias;
ccstd::optional<float> depthBiasClamp;
ccstd::optional<float> depthBiasSlop;
ccstd::optional<float> lineWidth;
void fromGFXRasterizerState(const gfx::RasterizerState &rs) {
isDiscard = rs.isDiscard;
isFrontFaceCCW = rs.isFrontFaceCCW;
depthBiasEnabled = rs.depthBiasEnabled;
isDepthClip = rs.isDepthClip;
isMultisample = rs.isMultisample;
polygonMode = rs.polygonMode;
shadeModel = rs.shadeModel;
cullMode = rs.cullMode;
depthBias = rs.depthBias;
depthBiasClamp = rs.depthBiasClamp;
depthBiasSlop = rs.depthBiasSlop;
lineWidth = rs.lineWidth;
}
void assignToGFXRasterizerState(gfx::RasterizerState &rs) const {
if (isDiscard.has_value()) {
rs.isDiscard = isDiscard.value();
}
if (isFrontFaceCCW.has_value()) {
rs.isFrontFaceCCW = isFrontFaceCCW.value();
}
if (depthBiasEnabled.has_value()) {
rs.depthBiasEnabled = depthBiasEnabled.value();
}
if (isDepthClip.has_value()) {
rs.isDepthClip = isDepthClip.value();
}
if (isMultisample.has_value()) {
rs.isMultisample = isMultisample.value();
}
if (polygonMode.has_value()) {
rs.polygonMode = polygonMode.value();
}
if (shadeModel.has_value()) {
rs.shadeModel = shadeModel.value();
}
if (cullMode.has_value()) {
rs.cullMode = cullMode.value();
}
if (depthBias.has_value()) {
rs.depthBias = depthBias.value();
}
if (depthBiasClamp.has_value()) {
rs.depthBiasClamp = depthBiasClamp.value();
}
if (depthBiasSlop.has_value()) {
rs.depthBiasSlop = depthBiasSlop.value();
}
if (lineWidth.has_value()) {
rs.lineWidth = lineWidth.value();
}
}
};
struct DepthStencilStateInfo {
ccstd::optional<bool> depthTest;
ccstd::optional<bool> depthWrite;
ccstd::optional<bool> stencilTestFront;
ccstd::optional<bool> stencilTestBack;
ccstd::optional<gfx::ComparisonFunc> depthFunc;
ccstd::optional<gfx::ComparisonFunc> stencilFuncFront;
ccstd::optional<uint32_t> stencilReadMaskFront;
ccstd::optional<uint32_t> stencilWriteMaskFront;
ccstd::optional<gfx::StencilOp> stencilFailOpFront;
ccstd::optional<gfx::StencilOp> stencilZFailOpFront;
ccstd::optional<gfx::StencilOp> stencilPassOpFront;
ccstd::optional<uint32_t> stencilRefFront;
ccstd::optional<gfx::ComparisonFunc> stencilFuncBack;
ccstd::optional<uint32_t> stencilReadMaskBack;
ccstd::optional<uint32_t> stencilWriteMaskBack;
ccstd::optional<gfx::StencilOp> stencilFailOpBack;
ccstd::optional<gfx::StencilOp> stencilZFailOpBack;
ccstd::optional<gfx::StencilOp> stencilPassOpBack;
ccstd::optional<uint32_t> stencilRefBack;
void fromGFXDepthStencilState(const gfx::DepthStencilState &ds) {
depthTest = ds.depthTest;
depthWrite = ds.depthWrite;
stencilTestFront = ds.stencilTestFront;
stencilTestBack = ds.stencilTestBack;
depthFunc = ds.depthFunc;
stencilFuncFront = ds.stencilFuncFront;
stencilReadMaskFront = ds.stencilReadMaskFront;
stencilWriteMaskFront = ds.stencilWriteMaskFront;
stencilFailOpFront = ds.stencilFailOpFront;
stencilZFailOpFront = ds.stencilZFailOpFront;
stencilPassOpFront = ds.stencilPassOpFront;
stencilRefFront = ds.stencilRefFront;
stencilFuncBack = ds.stencilFuncBack;
stencilReadMaskBack = ds.stencilReadMaskBack;
stencilWriteMaskBack = ds.stencilWriteMaskBack;
stencilFailOpBack = ds.stencilFailOpBack;
stencilZFailOpBack = ds.stencilZFailOpBack;
stencilPassOpBack = ds.stencilPassOpBack;
stencilRefBack = ds.stencilRefBack;
}
void assignToGFXDepthStencilState(gfx::DepthStencilState &ds) const {
if (depthTest.has_value()) {
ds.depthTest = depthTest.value();
}
if (depthWrite.has_value()) {
ds.depthWrite = depthWrite.value();
}
if (stencilTestFront.has_value()) {
ds.stencilTestFront = stencilTestFront.value();
}
if (stencilTestBack.has_value()) {
ds.stencilTestBack = stencilTestBack.value();
}
if (depthFunc.has_value()) {
ds.depthFunc = depthFunc.value();
}
if (stencilFuncFront.has_value()) {
ds.stencilFuncFront = stencilFuncFront.value();
}
if (stencilReadMaskFront.has_value()) {
ds.stencilReadMaskFront = stencilReadMaskFront.value();
}
if (stencilWriteMaskFront.has_value()) {
ds.stencilWriteMaskFront = stencilWriteMaskFront.value();
}
if (stencilFailOpFront.has_value()) {
ds.stencilFailOpFront = stencilFailOpFront.value();
}
if (stencilZFailOpFront.has_value()) {
ds.stencilZFailOpFront = stencilZFailOpFront.value();
}
if (stencilPassOpFront.has_value()) {
ds.stencilPassOpFront = stencilPassOpFront.value();
}
if (stencilRefFront.has_value()) {
ds.stencilRefFront = stencilRefFront.value();
}
if (stencilFuncBack.has_value()) {
ds.stencilFuncBack = stencilFuncBack.value();
}
if (stencilReadMaskBack.has_value()) {
ds.stencilReadMaskBack = stencilReadMaskBack.value();
}
if (stencilWriteMaskBack.has_value()) {
ds.stencilWriteMaskBack = stencilWriteMaskBack.value();
}
if (stencilFailOpBack.has_value()) {
ds.stencilFailOpBack = stencilFailOpBack.value();
}
if (stencilZFailOpBack.has_value()) {
ds.stencilZFailOpBack = stencilZFailOpBack.value();
}
if (stencilPassOpBack.has_value()) {
ds.stencilPassOpBack = stencilPassOpBack.value();
}
if (stencilRefBack.has_value()) {
ds.stencilRefBack = stencilRefBack.value();
}
}
};
struct BlendTargetInfo {
ccstd::optional<bool> blend;
ccstd::optional<gfx::BlendFactor> blendSrc;
ccstd::optional<gfx::BlendFactor> blendDst;
ccstd::optional<gfx::BlendOp> blendEq;
ccstd::optional<gfx::BlendFactor> blendSrcAlpha;
ccstd::optional<gfx::BlendFactor> blendDstAlpha;
ccstd::optional<gfx::BlendOp> blendAlphaEq;
ccstd::optional<gfx::ColorMask> blendColorMask;
void fromGFXBlendTarget(const gfx::BlendTarget &target) {
blend = target.blend;
blendSrc = target.blendSrc;
blendDst = target.blendDst;
blendEq = target.blendEq;
blendSrcAlpha = target.blendSrcAlpha;
blendDstAlpha = target.blendDstAlpha;
blendAlphaEq = target.blendAlphaEq;
blendColorMask = target.blendColorMask;
}
void assignToGFXBlendTarget(gfx::BlendTarget &target) const {
if (blend.has_value()) {
target.blend = blend.value();
}
if (blendSrc.has_value()) {
target.blendSrc = blendSrc.value();
}
if (blendDst.has_value()) {
target.blendDst = blendDst.value();
}
if (blendEq.has_value()) {
target.blendEq = blendEq.value();
}
if (blendSrcAlpha.has_value()) {
target.blendSrcAlpha = blendSrcAlpha.value();
}
if (blendDstAlpha.has_value()) {
target.blendDstAlpha = blendDstAlpha.value();
}
if (blendAlphaEq.has_value()) {
target.blendAlphaEq = blendAlphaEq.value();
}
if (blendColorMask.has_value()) {
target.blendColorMask = blendColorMask.value();
}
}
};
using BlendTargetInfoList = ccstd::vector<BlendTargetInfo>;
struct BlendStateInfo {
ccstd::optional<bool> isA2C;
ccstd::optional<bool> isIndepend;
ccstd::optional<gfx::Color> blendColor;
ccstd::optional<BlendTargetInfoList> targets;
void fromGFXBlendState(const gfx::BlendState &bs) {
isA2C = bs.isA2C;
isIndepend = bs.isIndepend;
blendColor = bs.blendColor;
size_t len = bs.targets.size();
if (len > 0) {
BlendTargetInfoList targetsList(len);
for (size_t i = 0; i < len; ++i) {
targetsList[i].fromGFXBlendTarget(bs.targets[i]);
}
targets = targetsList;
}
}
void assignToGFXBlendState(gfx::BlendState &bs) const {
if (targets.has_value()) {
const auto &targetsVal = targets.value();
bs.targets.resize(targetsVal.size());
for (size_t i = 0, len = targetsVal.size(); i < len; ++i) {
targetsVal[i].assignToGFXBlendTarget(bs.targets[i]);
}
}
if (isA2C.has_value()) {
bs.isA2C = isA2C.value();
}
if (isIndepend.has_value()) {
bs.isIndepend = isIndepend.value();
}
if (blendColor.has_value()) {
bs.blendColor = blendColor.value();
}
}
};
// Pass instance itself are compliant to IPassStates too
struct IPassStates {
ccstd::optional<int32_t> priority;
ccstd::optional<gfx::PrimitiveMode> primitive;
ccstd::optional<pipeline::RenderPassStage> stage;
ccstd::optional<RasterizerStateInfo> rasterizerState;
ccstd::optional<DepthStencilStateInfo> depthStencilState;
ccstd::optional<BlendStateInfo> blendState;
ccstd::optional<gfx::DynamicStateFlags> dynamicStates;
ccstd::optional<ccstd::string> phase;
ccstd::optional<ccstd::string> pass;
ccstd::optional<ccstd::string> subpass;
IPassStates() = default;
explicit IPassStates(const IPassInfoFull &o);
IPassStates &operator=(const IPassInfoFull &o);
void overrides(const IPassInfoFull &o);
};
using PassOverrides = IPassStates;
using PassPropertyInfoMap = PropertyContainer<ccstd::string, IPropertyInfo>;
struct IPassInfoFull final { // cjh } : public IPassInfo {
// IPassStates
ccstd::optional<int32_t> priority;
ccstd::optional<gfx::PrimitiveMode> primitive;
ccstd::optional<pipeline::RenderPassStage> stage;
ccstd::optional<RasterizerStateInfo> rasterizerState;
ccstd::optional<DepthStencilStateInfo> depthStencilState;
ccstd::optional<BlendStateInfo> blendState;
ccstd::optional<gfx::DynamicStateFlags> dynamicStates;
ccstd::optional<ccstd::string> phase;
ccstd::optional<ccstd::string> pass;
ccstd::optional<ccstd::string> subpass;
// IPassInfo
ccstd::string program; // auto-generated from 'vert' and 'frag'
ccstd::optional<MacroRecord> embeddedMacros;
ccstd::optional<index_t> propertyIndex; // NOTE: needs to use ccstd::optional<> since jsb should return 'undefined' instead of '-1' to avoid wrong value checking logic.
ccstd::optional<ccstd::string> switch_;
ccstd::optional<PassPropertyInfoMap> properties;
// IPassInfoFull
// generated part
index_t passIndex{0};
uint32_t passID = 0xFFFFFFFF;
uint32_t subpassID = 0xFFFFFFFF;
uint32_t phaseID = 0xFFFFFFFF;
MacroRecord defines;
ccstd::optional<PassOverrides> stateOverrides;
IPassInfoFull() = default;
explicit IPassInfoFull(const IPassStates &o) {
*this = o;
}
IPassInfoFull &operator=(const IPassStates &o) {
priority = o.priority;
primitive = o.primitive;
stage = o.stage;
rasterizerState = o.rasterizerState;
depthStencilState = o.depthStencilState;
blendState = o.blendState;
dynamicStates = o.dynamicStates;
phase = o.phase;
subpass = o.subpass;
return *this;
}
};
using IPassInfo = IPassInfoFull;
struct ITechniqueInfo {
ccstd::vector<IPassInfoFull> passes;
ccstd::optional<ccstd::string> name;
};
struct IBlockInfo {
uint32_t binding{UINT32_MAX};
ccstd::string name;
ccstd::vector<gfx::Uniform> members;
gfx::ShaderStageFlags stageFlags{gfx::ShaderStageFlags::NONE};
ccstd::vector<ccstd::string> defines;
};
struct ISamplerTextureInfo {
uint32_t binding{UINT32_MAX};
ccstd::string name;
gfx::Type type{gfx::Type::UNKNOWN};
uint32_t count{0};
gfx::ShaderStageFlags stageFlags{gfx::ShaderStageFlags::NONE};
ccstd::vector<ccstd::string> defines; // NOTE: used in Editor only
};
struct ITextureInfo {
uint32_t set{0};
uint32_t binding{UINT32_MAX};
ccstd::string name;
gfx::Type type{gfx::Type::UNKNOWN};
uint32_t count{0};
gfx::ShaderStageFlags stageFlags{gfx::ShaderStageFlags::NONE};
};
struct ISamplerInfo {
uint32_t set{0};
uint32_t binding{UINT32_MAX};
ccstd::string name;
uint32_t count{0};
gfx::ShaderStageFlags stageFlags{gfx::ShaderStageFlags::NONE};
};
struct IBufferInfo {
uint32_t binding{UINT32_MAX};
ccstd::string name;
gfx::MemoryAccess memoryAccess{gfx::MemoryAccess::NONE};
gfx::ShaderStageFlags stageFlags{gfx::ShaderStageFlags::NONE};
};
struct IImageInfo {
uint32_t binding{UINT32_MAX};
ccstd::string name;
gfx::Type type{gfx::Type::UNKNOWN};
uint32_t count{0};
gfx::MemoryAccess memoryAccess{gfx::MemoryAccess::NONE};
gfx::ShaderStageFlags stageFlags{gfx::ShaderStageFlags::NONE};
};
struct IInputAttachmentInfo {
uint32_t set{0};
uint32_t binding{UINT32_MAX};
ccstd::string name;
uint32_t count{0};
gfx::ShaderStageFlags stageFlags{gfx::ShaderStageFlags::NONE};
};
struct IAttributeInfo {
ccstd::string name;
gfx::Format format{gfx::Format::UNKNOWN};
bool isNormalized{false};
uint32_t stream{0U};
bool isInstanced{false};
uint32_t location{0U};
ccstd::vector<ccstd::string> defines;
};
struct IDefineInfo {
ccstd::string name;
ccstd::string type;
ccstd::optional<ccstd::vector<int32_t>> range; // cjh number is float? ?: number[];
ccstd::optional<ccstd::vector<ccstd::string>> options;
ccstd::optional<ccstd::string> defaultVal;
ccstd::optional<ccstd::vector<ccstd::string>> defines; // NOTE: it's only used in Editor
ccstd::optional<ccstd::unordered_map<ccstd::string, ccstd::variant<ccstd::string, bool>>> editor; // NOTE: it's only used in Editor
};
struct IBuiltin {
ccstd::string name;
ccstd::vector<ccstd::string> defines;
};
struct IBuiltinInfo {
ccstd::vector<IBuiltin> buffers;
ccstd::vector<IBuiltin> blocks;
ccstd::vector<IBuiltin> samplerTextures;
ccstd::vector<IBuiltin> images;
};
using BuiltinsStatisticsType = ccstd::unordered_map<ccstd::string, int32_t>;
struct IBuiltins {
IBuiltinInfo globals;
IBuiltinInfo locals;
BuiltinsStatisticsType statistics;
};
struct IDescriptorInfo {
uint32_t rate{0};
ccstd::vector<IBlockInfo> blocks;
ccstd::vector<ISamplerTextureInfo> samplerTextures;
ccstd::vector<ISamplerInfo> samplers;
ccstd::vector<ITextureInfo> textures;
ccstd::vector<IBufferInfo> buffers;
ccstd::vector<IImageInfo> images;
ccstd::vector<IInputAttachmentInfo> subpassInputs;
};
struct IShaderSource {
ccstd::string vert;
ccstd::string frag;
ccstd::optional<ccstd::string> compute;
};
struct IShaderInfo {
ccstd::string name;
ccstd::hash_t hash{gfx::INVALID_SHADER_HASH};
IShaderSource glsl4;
IShaderSource glsl3;
IShaderSource glsl1;
IBuiltins builtins;
ccstd::vector<IDefineInfo> defines;
ccstd::vector<IAttributeInfo> attributes;
ccstd::vector<IBlockInfo> blocks;
ccstd::vector<ISamplerTextureInfo> samplerTextures;
ccstd::vector<ISamplerInfo> samplers;
ccstd::vector<ITextureInfo> textures;
ccstd::vector<IBufferInfo> buffers;
ccstd::vector<IImageInfo> images;
ccstd::vector<IInputAttachmentInfo> subpassInputs;
ccstd::vector<IDescriptorInfo> descriptors;
const IShaderSource *getSource(const ccstd::string &version) const {
if (version == "glsl1") return &glsl1;
if (version == "glsl3") return &glsl3;
if (version == "glsl4") return &glsl4;
return nullptr;
}
IShaderSource *getSource(const ccstd::string &version) {
if (version == "glsl1") return &glsl1;
if (version == "glsl3") return &glsl3;
if (version == "glsl4") return &glsl4;
return nullptr;
}
};
using IPreCompileInfoValueType = ccstd::variant<ccstd::monostate, ccstd::vector<bool>, ccstd::vector<int32_t>, ccstd::vector<ccstd::string>>;
using IPreCompileInfo = ccstd::unordered_map<ccstd::string, IPreCompileInfoValueType>;
class EffectAsset final : public Asset {
public:
using Super = Asset;
EffectAsset() = default;
~EffectAsset() override = default;
/**
* @en Register the effect asset to the static map
* @zh 将指定 effect 注册到全局管理器。
*/
static void registerAsset(EffectAsset *asset);
/**
* @en Unregister the effect asset from the static map
* @zh 将指定 effect 从全局管理器移除。
*/
static void remove(const ccstd::string &name);
static void remove(EffectAsset *asset);
/**
* @en Get the effect asset by the given name.
* @zh 获取指定名字的 effect 资源。
*/
static EffectAsset *get(const ccstd::string &name);
using RegisteredEffectAssetMap = ccstd::unordered_map<ccstd::string, IntrusivePtr<EffectAsset>>;
/**
* @en Get all registered effect assets.
* @zh 获取所有已注册的 effect 资源。
*/
static RegisteredEffectAssetMap &getAll() { return EffectAsset::effects; }
static bool isLayoutValid() { return layoutValid; }
static void setLayoutValid() { layoutValid = true; }
inline void setTechniques(const ccstd::vector<ITechniqueInfo> &val) { _techniques = val; }
inline void setShaders(const ccstd::vector<IShaderInfo> &val) { _shaders = val; }
inline void setCombinations(const ccstd::vector<IPreCompileInfo> &val) { _combinations = val; }
inline const ccstd::vector<ITechniqueInfo> &getTechniques() const { return _techniques; }
inline const ccstd::vector<IShaderInfo> &getShaders() const { return _shaders; }
inline const ccstd::vector<IPreCompileInfo> &getCombinations() const { return _combinations; }
/*
@serializable
@editorOnly
*/
bool hideInEditor = false;
/**
* @en The loaded callback which should be invoked by the [[Loader]], will automatically register the effect.
* @zh 通过 [[Loader]] 加载完成时的回调,将自动注册 effect 资源。
*/
void onLoaded() override;
bool destroy() override;
void initDefault(const ccstd::optional<ccstd::string> &uuid) override;
bool validate() const override;
protected:
BaseEngine::EngineStatusChange::EventID _engineEventId;
static ccstd::vector<MacroRecord> doCombine(const ccstd::vector<MacroRecord> &cur, const IPreCompileInfo &info, IPreCompileInfo::iterator iter);
static ccstd::vector<MacroRecord> generateRecords(const ccstd::string &key, const IPreCompileInfoValueType &value);
static ccstd::vector<MacroRecord> insertInfoValue(const ccstd::vector<MacroRecord> &records,
const ccstd::string &key,
const IPreCompileInfoValueType &value);
void precompile();
// We need make it to public for deserialization
public:
/**
* @en The techniques used by the current effect.
* @zh 当前 effect 的所有可用 technique。
@serializable
@editable*/
ccstd::vector<ITechniqueInfo> _techniques;
/**
* @en The shaders used by the current effect.
* @zh 当前 effect 使用的所有 shader。
@serializable
@editable*/
ccstd::vector<IShaderInfo> _shaders;
/**
* @en The preprocess macro combinations for the shader
* @zh 每个 shader 需要预编译的宏定义组合。
@serializable
@editable*/
ccstd::vector<IPreCompileInfo> _combinations;
//
protected:
static RegisteredEffectAssetMap effects; // cjh TODO: how to clear when game exits.
static bool layoutValid;
CC_DISALLOW_COPY_MOVE_ASSIGN(EffectAsset);
friend class EffectAssetDeserializer;
friend class Material;
friend class ProgramLib;
friend class MaterialInstance;
friend class BuiltinResMgr;
};
} // namespace cc

116
cocos/core/assets/Font.cpp Normal file
View File

@@ -0,0 +1,116 @@
/****************************************************************************
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 "Font.h"
#include <algorithm>
#include <cctype>
#include "base/Data.h"
#include "base/Log.h"
#include "base/Macros.h"
#include "base/memory/Memory.h"
#include "base/std/hash/hash.h"
#include "gfx-base/GFXTexture.h"
#include "math/Math.h"
#include "platform/FileUtils.h"
#include "profiler/Profiler.h"
namespace cc {
ccstd::hash_t KerningHash::operator()(const KerningPair &k) const {
ccstd::hash_t seed = 2;
ccstd::hash_combine(seed, k.prevCode);
ccstd::hash_combine(seed, k.nextCode);
return seed;
}
/**
* FontFaceInfo
*/
FontFaceInfo::FontFaceInfo(uint32_t size)
: fontSize(size) {}
FontFaceInfo::FontFaceInfo(uint32_t size, uint32_t width, uint32_t height)
: fontSize(size), textureWidth(width), textureHeight(height) {
CC_ASSERT(math::isPowerOfTwo(width) && math::isPowerOfTwo(height)); // Font texture size must be power of 2.
// preload digit & alphabet characters by default
for (auto i = 0U; i < 128; i++) {
if (std::isdigit(i) || std::isalpha(i)) {
preLoadedCharacters.emplace_back(i);
}
}
}
FontFaceInfo::FontFaceInfo(uint32_t size, uint32_t width, uint32_t height, ccstd::vector<uint32_t> chars)
: fontSize(size), textureWidth(width), textureHeight(height), preLoadedCharacters(std::move(chars)) {
CC_ASSERT(math::isPowerOfTwo(width) && math::isPowerOfTwo(height)); // Font texture size must be power of 2.
}
/**
* FontFace
*/
FontFace::FontFace(Font *font)
: _font(font) {
}
FontFace::~FontFace() {
for (auto *texture : _textures) {
CC_SAFE_DESTROY_AND_DELETE(texture);
}
}
/**
* Font
*/
Font::Font(FontType type, const ccstd::string &path)
: _type(type), _path(path) {
load(path);
}
Font::~Font() {
releaseFaces();
CC_PROFILE_MEMORY_DEC(Font, _data.size());
}
void Font::load(const ccstd::string &path) {
Data data = FileUtils::getInstance()->getDataFromFile(path);
if (data.isNull()) {
CC_LOG_WARNING("Font load failed, path: %s.", path.c_str());
return;
}
_data.resize(data.getSize());
memcpy(&_data[0], data.getBytes(), data.getSize());
CC_PROFILE_MEMORY_INC(Font, _data.size());
}
void Font::releaseFaces() {
for (auto &iter : _faces) {
delete iter.second;
}
_faces.clear();
}
} // namespace cc

157
cocos/core/assets/Font.h Normal file
View File

@@ -0,0 +1,157 @@
/****************************************************************************
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/container/unordered_map.h"
#include "base/std/container/vector.h"
#include "core/assets/Asset.h"
namespace cc {
namespace gfx {
class Texture;
}
class Font;
constexpr uint32_t DEFAULT_FREETYPE_TEXTURE_SIZE = 512U;
constexpr uint32_t MIN_FONT_SIZE = 1U;
constexpr uint32_t MAX_FONT_SIZE = 128U;
enum class FontType {
INVALID,
FREETYPE,
BITMAP,
};
struct FontGlyph {
int16_t x{0};
int16_t y{0};
uint16_t width{0U};
uint16_t height{0U};
int16_t bearingX{0};
int16_t bearingY{0};
int32_t advance{0};
uint32_t page{0U}; // index of textures
};
struct KerningPair {
uint32_t prevCode{0U};
uint32_t nextCode{0U};
inline bool operator==(const KerningPair &k) const noexcept {
return prevCode == k.prevCode && nextCode == k.nextCode;
}
inline bool operator!=(const KerningPair &k) const noexcept {
return !(*this == k);
}
};
struct KerningHash {
ccstd::hash_t operator()(const KerningPair &k) const;
};
/**
* FontFaceInfo
*/
struct FontFaceInfo {
explicit FontFaceInfo(uint32_t size);
FontFaceInfo(uint32_t size, uint32_t width, uint32_t height);
FontFaceInfo(uint32_t size, uint32_t width, uint32_t height, ccstd::vector<uint32_t> chars);
// only used in freetype, for bitmap font, fontSize is determined by file.
uint32_t fontSize{1U};
uint32_t textureWidth{DEFAULT_FREETYPE_TEXTURE_SIZE};
uint32_t textureHeight{DEFAULT_FREETYPE_TEXTURE_SIZE};
ccstd::vector<uint32_t> preLoadedCharacters;
//~
};
/**
* FontFace
*/
class FontFace {
public:
explicit FontFace(Font *font);
virtual ~FontFace();
FontFace(const FontFace &) = delete;
FontFace(FontFace &&) = delete;
FontFace &operator=(const FontFace &) = delete;
FontFace &operator=(FontFace &&) = delete;
virtual const FontGlyph *getGlyph(uint32_t code) = 0;
virtual float getKerning(uint32_t prevCode, uint32_t nextCode) = 0;
inline Font *getFont() const { return _font; }
inline uint32_t getFontSize() const { return _fontSize; }
inline uint32_t getLineHeight() const { return _lineHeight; }
inline const ccstd::vector<gfx::Texture *> &getTextures() const { return _textures; }
inline gfx::Texture *getTexture(uint32_t page) const { return _textures[page]; }
inline uint32_t getTextureWidth() const { return _textureWidth; }
inline uint32_t getTextureHeight() const { return _textureHeight; }
protected:
virtual void doInit(const FontFaceInfo &info) = 0;
Font *_font{nullptr};
uint32_t _fontSize{1U};
uint32_t _lineHeight{0U};
ccstd::unordered_map<uint32_t, FontGlyph> _glyphs;
ccstd::unordered_map<KerningPair, float, KerningHash> _kernings;
ccstd::vector<gfx::Texture *> _textures;
uint32_t _textureWidth{0U};
uint32_t _textureHeight{0U};
};
/**
* Font
*/
class Font : public Asset {
public:
Font(FontType type, const ccstd::string &path);
~Font() override;
Font(const Font &) = delete;
Font(Font &&) = delete;
Font &operator=(const Font &) = delete;
Font &operator=(Font &&) = delete;
virtual FontFace *createFace(const FontFaceInfo &info) = 0;
inline FontType getType() const { return _type; }
inline const ccstd::string &getPath() const { return _path; }
inline const ccstd::vector<uint8_t> &getData() const { return _data; }
inline FontFace *getFace(uint32_t fontSize) { return _faces[fontSize]; }
void releaseFaces();
protected:
void load(const ccstd::string &path);
FontType _type{FontType::INVALID};
ccstd::string _path;
ccstd::vector<uint8_t> _data;
ccstd::unordered_map<uint32_t, FontFace *> _faces;
};
} // namespace cc

View File

@@ -0,0 +1,316 @@
/****************************************************************************
Copyright (c) 2021-2023 Xiamen Yaji Software Co., Ltd.
Portions of this software are copyright ? <2022> The FreeType
Project (www.freetype.org). All rights reserved.
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 "FreeTypeFont.h"
#include <freetype/ft2build.h>
#include FT_FREETYPE_H
#include <cstdint>
#include "base/Log.h"
#include "gfx-base/GFXDevice.h"
namespace cc {
/**
* FTLibrary
*/
struct FTLibrary {
FTLibrary() {
FT_Error error = FT_Init_FreeType(&lib);
if (error) {
CC_LOG_ERROR("FreeType init failed, error code: %d.", error);
}
}
~FTLibrary() {
if (lib) {
FT_Error error = FT_Done_FreeType(lib);
if (error) {
CC_LOG_ERROR("FreeType exit failed, error code: %d.", error);
}
lib = nullptr;
}
}
FT_Library lib{nullptr};
};
/**
* FTFace
*/
struct FTFace {
explicit FTFace(FT_Face f)
: face(f) {
}
~FTFace() {
if (face) {
FT_Done_Face(face);
face = nullptr;
}
}
FT_Face face{nullptr};
};
/**
* GlyphAllocator: allocate space for glyph from top to down, from left to right.
*/
class GlyphAllocator {
public:
GlyphAllocator(uint32_t maxWidth, uint32_t maxHeight)
: _maxWidth(maxWidth), _maxHeight(maxHeight) {}
inline void reset() {
_nextX = 0U;
_nextY = 0U;
_maxLineHeight = 0U;
}
bool allocate(uint32_t width, uint32_t height, uint32_t &x, uint32_t &y) {
// try current line
if (_nextX + width <= _maxWidth && _nextY + height <= _maxHeight) {
x = _nextX;
y = _nextY;
_nextX += width;
_maxLineHeight = std::max(_maxLineHeight, height);
return true;
}
// try next line
uint32_t nextY = _nextY + _maxLineHeight;
if (width <= _maxWidth && nextY + height <= _maxHeight) {
x = 0U;
y = nextY;
_nextX = width;
_nextY = nextY;
_maxLineHeight = height;
return true;
}
return false;
}
private:
// texture resolution
const uint32_t _maxWidth{0U};
const uint32_t _maxHeight{0U};
// next space
uint32_t _nextX{0U};
uint32_t _nextY{0U};
// max height of current line
uint32_t _maxLineHeight{0U};
};
/**
* FreeTypeFontFace
*/
FTLibrary *FreeTypeFontFace::library = nullptr;
FreeTypeFontFace::FreeTypeFontFace(Font *font)
: FontFace(font) {
if (!library) {
library = ccnew FTLibrary();
}
}
void FreeTypeFontFace::doInit(const FontFaceInfo &info) {
const auto &fontData = _font->getData();
if (fontData.empty()) {
CC_LOG_ERROR("FreeTypeFontFace doInit failed: empty font data.");
return;
}
_fontSize = info.fontSize < MIN_FONT_SIZE ? MIN_FONT_SIZE : (info.fontSize > MAX_FONT_SIZE ? MAX_FONT_SIZE : info.fontSize);
_textureWidth = info.textureWidth;
_textureHeight = info.textureHeight;
_allocator = std::make_unique<GlyphAllocator>(_textureWidth, _textureHeight);
FT_Face face{nullptr};
FT_Error error = FT_New_Memory_Face(library->lib, fontData.data(), static_cast<FT_Long>(fontData.size()), 0, &face);
if (error) {
CC_LOG_ERROR("FT_New_Memory_Face failed, error code: %d.", error);
return;
}
error = FT_Set_Pixel_Sizes(face, 0, _fontSize);
if (error) {
CC_LOG_ERROR("FT_Set_Pixel_Sizes failed, error code: %d.", error);
return;
}
_face = std::make_unique<FTFace>(face);
_lineHeight = static_cast<uint32_t>(face->size->metrics.height >> 6);
for (const auto &code : info.preLoadedCharacters) {
loadGlyph(code);
}
}
const FontGlyph *FreeTypeFontFace::getGlyph(uint32_t code) {
auto iter = _glyphs.find(code);
if (iter != _glyphs.end()) {
return &iter->second;
}
return loadGlyph(code);
}
float FreeTypeFontFace::getKerning(uint32_t prevCode, uint32_t nextCode) {
FT_Face face = _face->face;
if (!FT_HAS_KERNING(face)) {
return 0.0F;
}
const auto &iter = _kernings.find({prevCode, nextCode});
if (iter != _kernings.end()) {
return iter->second;
}
FT_Vector kerning;
FT_UInt prevIndex = FT_Get_Char_Index(face, prevCode);
FT_UInt nextIndex = FT_Get_Char_Index(face, nextCode);
FT_Error error = FT_Get_Kerning(face, prevIndex, nextIndex, FT_KERNING_DEFAULT, &kerning);
if (error) {
CC_LOG_WARNING("FT_Get_Kerning failed, error code: %d, prevCode: %d, nextCode: %d", error, prevCode, nextCode);
return 0.0F;
}
auto result = static_cast<float>(kerning.x >> 6);
_kernings[{prevCode, nextCode}] = result;
return result;
}
const FontGlyph *FreeTypeFontFace::loadGlyph(uint32_t code) {
FT_Face face = _face->face;
FT_Error error = FT_Load_Char(face, code, FT_LOAD_RENDER);
if (error) {
CC_LOG_WARNING("FT_Load_Char failed, error code: %d, character: %u.", error, code);
return nullptr;
}
FontGlyph glyph;
glyph.width = face->glyph->bitmap.width;
glyph.height = face->glyph->bitmap.rows;
glyph.bearingX = face->glyph->bitmap_left;
glyph.bearingY = face->glyph->bitmap_top;
glyph.advance = static_cast<int32_t>(face->glyph->advance.x >> 6); // advance.x's unit is 1/64 pixels
uint32_t x = 0U;
uint32_t y = 0U;
if (_textures.empty()) {
createTexture(_textureWidth, _textureHeight);
}
if (glyph.width > 0U && glyph.height > 0U) {
// try current texture
if (!_allocator->allocate(glyph.width + 1, glyph.height + 1, x, y)) {
createTexture(_textureWidth, _textureHeight);
// try new empty texture
_allocator->reset();
if (!_allocator->allocate(glyph.width + 1, glyph.height + 1, x, y)) {
CC_LOG_WARNING("Glyph allocate failed, character: %u.", code);
return nullptr;
}
}
auto page = static_cast<uint32_t>(_textures.size() - 1);
updateTexture(page, x, y, glyph.width, glyph.height, face->glyph->bitmap.buffer);
glyph.x = x;
glyph.y = y;
glyph.page = page;
}
_glyphs[code] = glyph;
return &_glyphs[code];
}
void FreeTypeFontFace::createTexture(uint32_t width, uint32_t height) {
auto *device = gfx::Device::getInstance();
auto *texture = device->createTexture({gfx::TextureType::TEX2D,
gfx::TextureUsageBit::SAMPLED | gfx::TextureUsageBit::TRANSFER_DST,
gfx::Format::R8,
width,
height});
_textures.push_back(texture);
std::vector<uint8_t> empty(width * height, 0);
auto page = static_cast<uint32_t>(_textures.size() - 1);
updateTexture(page, 0U, 0U, width, height, empty.data());
}
void FreeTypeFontFace::updateTexture(uint32_t page, uint32_t x, uint32_t y, uint32_t width, uint32_t height, const uint8_t *buffer) {
auto *texture = getTexture(page);
gfx::BufferDataList buffers{buffer};
gfx::BufferTextureCopyList regions = {{0U,
0U,
0U,
{static_cast<int32_t>(x), static_cast<int32_t>(y), 0U},
{width, height, 1U},
{0U, 0U, 1U}}};
auto *device = gfx::Device::getInstance();
device->copyBuffersToTexture(buffers, texture, regions);
}
void FreeTypeFontFace::destroyFreeType() {
if (library) {
delete library;
library = nullptr;
}
}
/**
* FreeTypeFont
*/
FreeTypeFont::FreeTypeFont(const ccstd::string &path)
: Font(FontType::FREETYPE, path) {
}
FontFace *FreeTypeFont::createFace(const FontFaceInfo &info) {
auto *face = ccnew FreeTypeFontFace(this);
face->doInit(info);
uint32_t fontSize = face->getFontSize();
_faces[fontSize] = face;
return face;
}
} // namespace cc

View File

@@ -0,0 +1,85 @@
/****************************************************************************
Copyright (c) 2021-2023 Xiamen Yaji Software Co., Ltd.
Portions of this software are copyright ? <2022> The FreeType
Project (www.freetype.org). All rights reserved.
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 <memory>
#include "Font.h"
#include "base/std/container/string.h"
namespace cc {
struct FTLibrary;
struct FTFace;
class GlyphAllocator;
/**
* FreeTypeFontFace
*/
class FreeTypeFontFace : public FontFace {
public:
explicit FreeTypeFontFace(Font *font);
~FreeTypeFontFace() override = default;
FreeTypeFontFace(const FreeTypeFontFace &) = delete;
FreeTypeFontFace(FreeTypeFontFace &&) = delete;
FreeTypeFontFace &operator=(const FreeTypeFontFace &) = delete;
FreeTypeFontFace &operator=(FreeTypeFontFace &&) = delete;
const FontGlyph *getGlyph(uint32_t code) override;
float getKerning(uint32_t prevCode, uint32_t nextCode) override;
static void destroyFreeType();
private:
void doInit(const FontFaceInfo &info) override;
const FontGlyph *loadGlyph(uint32_t code);
void createTexture(uint32_t width, uint32_t height);
void updateTexture(uint32_t page, uint32_t x, uint32_t y, uint32_t width, uint32_t height, const uint8_t *buffer);
std::unique_ptr<GlyphAllocator> _allocator{nullptr};
std::unique_ptr<FTFace> _face;
static FTLibrary *library;
friend class FreeTypeFont;
};
/**
* FreeTypeFont
*/
class FreeTypeFont : public Font {
public:
explicit FreeTypeFont(const ccstd::string &path);
~FreeTypeFont() override = default;
FreeTypeFont(const FreeTypeFont &) = delete;
FreeTypeFont(FreeTypeFont &&) = delete;
FreeTypeFont &operator=(const FreeTypeFont &) = delete;
FreeTypeFont &operator=(FreeTypeFont &&) = delete;
FontFace *createFace(const FontFaceInfo &info) override;
private:
};
} // namespace cc

View File

@@ -0,0 +1,137 @@
/****************************************************************************
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 "core/assets/ImageAsset.h"
#include "platform/Image.h"
#include "base/Log.h"
namespace cc {
ImageAsset::~ImageAsset() {
if (_needFreeData && _data) {
free(_data);
}
}
void ImageAsset::setNativeAsset(const ccstd::any &obj) {
if (obj.has_value()) {
auto **pImage = const_cast<Image **>(ccstd::any_cast<Image *>(&obj));
if (pImage != nullptr) {
Image *image = *pImage;
image->takeData(&_data);
_needFreeData = true;
_width = image->getWidth();
_height = image->getHeight();
_format = static_cast<PixelFormat>(image->getRenderFormat());
_url = image->getFilePath();
_mipmapLevelDataSize = image->getMipmapLevelDataSize();
} else {
const auto *imageSource = ccstd::any_cast<IMemoryImageSource>(&obj);
if (imageSource != nullptr) {
_arrayBuffer = imageSource->data;
_data = const_cast<uint8_t *>(_arrayBuffer->getData());
_width = imageSource->width;
_height = imageSource->height;
_format = imageSource->format;
_mipmapLevelDataSize = imageSource->mipmapLevelDataSize;
} else {
CC_LOG_WARNING("ImageAsset::setNativeAsset, unknown type!");
}
}
}
}
IntrusivePtr<ImageAsset> ImageAsset::extractMipmap0() {
auto *res = new ImageAsset;
res->_data = nullptr;
res->_needFreeData = false;
res->_width = _width;
res->_height = _height;
res->_format = _format;
res->_uuid = _uuid;
res->_data = _data;
return res;
}
std::vector<IntrusivePtr<ImageAsset>> ImageAsset::extractMipmaps() {
std::vector<IntrusivePtr<ImageAsset>> res{};
if (!_mipmapLevelDataSize.empty()) {
size_t offset = 0UL;
auto height = _height;
auto width = _width;
for (auto mipmapSize : _mipmapLevelDataSize) {
auto *mipmap = new ImageAsset;
mipmap->_data = _data + offset;
mipmap->_needFreeData = false;
mipmap->_width = width;
mipmap->_height = height;
mipmap->_format = _format;
mipmap->_uuid = _uuid;
offset += mipmapSize;
width = std::max(width >> 1, 1U);
height = std::max(height >> 1, 1U);
res.emplace_back(mipmap);
}
} else {
res.emplace_back(this);
}
return res;
}
const uint8_t *ImageAsset::getData() const {
return _data;
}
uint32_t ImageAsset::getWidth() const {
return _width;
}
uint32_t ImageAsset::getHeight() const {
return _height;
}
PixelFormat ImageAsset::getFormat() const {
return _format;
}
const std::vector<uint32_t> &ImageAsset::getMipmapLevelDataSize() const {
return _mipmapLevelDataSize;
}
bool ImageAsset::isCompressed() const {
return (_format >= PixelFormat::RGB_ETC1 && _format <= PixelFormat::RGBA_ASTC_12X12) || (_format >= PixelFormat::RGB_A_PVRTC_2BPPV1 && _format <= PixelFormat::RGBA_ETC1);
}
const ccstd::string &ImageAsset::getUrl() const {
return _url;
}
} // namespace cc

View File

@@ -0,0 +1,138 @@
/****************************************************************************
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 "core/assets/Asset.h"
#include "core/assets/AssetEnum.h"
#include "core/ArrayBuffer.h"
namespace cc {
class Image;
/**
* @en Image source in memory
* @zh 内存图像源。
*/
struct IMemoryImageSource {
ArrayBuffer::Ptr data;
bool compressed{false};
uint32_t width{0};
uint32_t height{0};
PixelFormat format{PixelFormat::RGBA8888};
ccstd::vector<uint32_t> mipmapLevelDataSize;
};
/**
* @en Image Asset.
* @zh 图像资源。
*/
class ImageAsset final : public Asset {
public:
using Super = Asset;
ImageAsset() = default;
~ImageAsset() override;
//minggo: do not need it in c++.
// ccstd::any getNativeAsset() const override { return ccstd::any(_nativeData); }
void setNativeAsset(const ccstd::any &obj) override;
/**
* @en Image data.
* @zh 此图像资源的图像数据。
*/
const uint8_t *getData() const;
/**
* @en The pixel width of the image.
* @zh 此图像资源的像素宽度。
*/
uint32_t getWidth() const;
/**
* @en The pixel height of the image.
* @zh 此图像资源的像素高度。
*/
uint32_t getHeight() const;
/**
* @en The pixel format of the image.
* @zh 此图像资源的像素格式。
*/
PixelFormat getFormat() const;
/**
* @en The pixel mipmap level data size of the image.
* @zh 此图像资源的mipmap层级大小。
*/
const ccstd::vector<uint32_t> &getMipmapLevelDataSize() const;
/**
* @en Whether the image is in compressed texture format.
* @zh 此图像资源是否为压缩像素格式。
*/
bool isCompressed() const;
/**
* @en The original source image URL, it could be empty.
* @zh 此图像资源的原始图像源的 URL。当原始图像元不是 HTML 文件时可能为空。
* @deprecated Please use [[nativeUrl]]
*/
const ccstd::string &getUrl() const;
// Functions for TS.
inline void setWidth(uint32_t width) { _width = width; }
inline void setHeight(uint32_t height) { _height = height; }
inline void setFormat(PixelFormat format) { _format = format; }
inline void setData(uint8_t *data) { _data = data; }
inline void setNeedFreeData(bool v) { _needFreeData = v; }
inline void setUrl(const ccstd::string &url) { _url = url; }
inline void setMipmapLevelDataSize(const ccstd::vector<uint32_t> &mipmapLevelDataSize) { _mipmapLevelDataSize = mipmapLevelDataSize; }
// Functions for Utils.
IntrusivePtr<ImageAsset> extractMipmap0();
std::vector<IntrusivePtr<ImageAsset>> extractMipmaps();
private:
uint8_t *_data{nullptr};
PixelFormat _format{PixelFormat::RGBA8888};
uint32_t _width{0};
uint32_t _height{0};
bool _needFreeData{false}; // Should free data if the data is assigned in C++.
ArrayBuffer::Ptr _arrayBuffer; //minggo: hold the data from ImageSource.
ccstd::string _url;
ccstd::vector<uint32_t> _mipmapLevelDataSize;
CC_DISALLOW_COPY_MOVE_ASSIGN(ImageAsset);
};
} // namespace cc

View File

@@ -0,0 +1,447 @@
/****************************************************************************
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 "core/assets/Material.h"
#include "base/Utils.h"
#include "core/Root.h"
#include "core/assets/EffectAsset.h"
#include "core/builtin/BuiltinResMgr.h"
#include "core/platform/Debug.h"
#include "math/Color.h"
#include "renderer/pipeline/helper/Utils.h"
#include "scene/Pass.h"
namespace cc {
/* static */
ccstd::hash_t Material::getHashForMaterial(Material *material) {
if (material == nullptr) {
return 0;
}
ccstd::hash_t hash = 0U;
const auto &passes = *material->_passes;
for (const auto &pass : passes) {
hash ^= pass->getHash();
}
return hash;
}
Material::Material() {
_passes = std::make_shared<ccstd::vector<IntrusivePtr<scene::Pass>>>();
}
Material::~Material() = default;
void Material::initialize(const IMaterialInfo &info) {
auto &passes = *_passes;
if (!passes.empty()) {
debug::warnID(12005);
return;
}
if (!_defines.empty()) {
_defines.clear();
}
if (!_states.empty()) {
_states.clear();
}
if (!_props.empty()) {
_props.clear();
}
fillInfo(info);
update();
}
void Material::reset(const IMaterialInfo &info) { // to be consistent with other assets
initialize(info);
}
bool Material::destroy() {
doDestroy();
return Super::destroy();
}
void Material::doDestroy() {
auto &passes = *_passes;
if (!passes.empty()) {
for (const auto &pass : passes) {
pass->destroy();
}
}
passes.clear();
emit<PassesUpdated>();
}
void Material::recompileShaders(const MacroRecord & /*overrides*/, index_t /*passIdx*/) {
CC_ABORT();
CC_LOG_WARNING("Shaders in material asset '%s' cannot be modified at runtime, please instantiate the material first.", _name.c_str());
}
void Material::overridePipelineStates(const PassOverrides & /*overrides*/, index_t /*passIdx*/) {
CC_ABORT();
CC_LOG_WARNING("Pipeline states in material asset '%s' cannot be modified at runtime, please instantiate the material first.", _name.c_str());
}
void Material::onLoaded() {
update();
}
void Material::resetUniforms(bool clearPasses /* = true */) {
const auto &passes = *_passes;
_props.resize(passes.size());
if (!clearPasses) {
return;
}
for (const auto &pass : passes) {
pass->resetUBOs();
pass->resetTextures();
}
}
void Material::setProperty(const ccstd::string &name, const MaterialPropertyVariant &val, index_t passIdx /* = CC_INVALID_INDEX */) {
const auto &passes = *_passes;
bool success = false;
if (passIdx == CC_INVALID_INDEX) { // try set property for all applicable passes
size_t len = passes.size();
for (size_t i = 0; i < len; i++) {
const auto &pass = passes[i];
if (uploadProperty(pass, name, val)) {
_props[pass->getPropertyIndex()][name] = val;
success = true;
}
}
} else {
if (passIdx >= passes.size()) {
CC_LOG_WARNING("illegal pass index: %d.", passIdx);
return;
}
const auto &pass = passes[passIdx];
if (uploadProperty(pass, name, val)) {
_props[pass->getPropertyIndex()][name] = val;
success = true;
}
}
if (!success) {
CC_LOG_WARNING("illegal property name: %s.", name.c_str());
}
}
void Material::setPropertyNull(const ccstd::string &name, index_t passIdx) {
MaterialPropertyVariant val;
setProperty(name, val, passIdx);
}
#define CC_MATERIAL_SETPROPERTY_IMPL(funcNameSuffix, type) \
void Material::setProperty##funcNameSuffix(const ccstd::string &name, type val, index_t passIdx /* = CC_INVALID_INDEX*/) { \
setProperty(name, val, passIdx); \
}
CC_MATERIAL_SETPROPERTY_IMPL(Float32, float)
CC_MATERIAL_SETPROPERTY_IMPL(Int32, int32_t)
CC_MATERIAL_SETPROPERTY_IMPL(Vec2, const Vec2 &)
CC_MATERIAL_SETPROPERTY_IMPL(Vec3, const Vec3 &)
CC_MATERIAL_SETPROPERTY_IMPL(Vec4, const Vec4 &)
CC_MATERIAL_SETPROPERTY_IMPL(Color, const cc::Color &)
CC_MATERIAL_SETPROPERTY_IMPL(Mat3, const Mat3 &)
CC_MATERIAL_SETPROPERTY_IMPL(Mat4, const Mat4 &)
CC_MATERIAL_SETPROPERTY_IMPL(Quaternion, const Quaternion &)
CC_MATERIAL_SETPROPERTY_IMPL(TextureBase, TextureBase *)
CC_MATERIAL_SETPROPERTY_IMPL(GFXTexture, gfx::Texture *)
#undef CC_MATERIAL_SETPROPERTY_IMPL
#define CC_MATERIAL_SETPROPERTY_ARRAY_IMPL(funcNameSuffix, type) \
void Material::setProperty##funcNameSuffix##Array(const ccstd::string &name, const ccstd::vector<type> &val, index_t /*passIdx = CC_INVALID_INDEX*/) { \
MaterialPropertyList propertyArr; \
propertyArr.reserve(val.size()); \
for (const auto &e : val) { \
propertyArr.emplace_back(e); \
} \
setProperty(name, propertyArr); \
}
CC_MATERIAL_SETPROPERTY_ARRAY_IMPL(Float32, float)
CC_MATERIAL_SETPROPERTY_ARRAY_IMPL(Int32, int32_t)
CC_MATERIAL_SETPROPERTY_ARRAY_IMPL(Vec2, Vec2)
CC_MATERIAL_SETPROPERTY_ARRAY_IMPL(Vec3, Vec3)
CC_MATERIAL_SETPROPERTY_ARRAY_IMPL(Vec4, Vec4)
CC_MATERIAL_SETPROPERTY_ARRAY_IMPL(Color, Color)
CC_MATERIAL_SETPROPERTY_ARRAY_IMPL(Mat3, Mat3)
CC_MATERIAL_SETPROPERTY_ARRAY_IMPL(Mat4, Mat4)
CC_MATERIAL_SETPROPERTY_ARRAY_IMPL(Quaternion, Quaternion)
CC_MATERIAL_SETPROPERTY_ARRAY_IMPL(TextureBase, TextureBase *)
CC_MATERIAL_SETPROPERTY_ARRAY_IMPL(GFXTexture, gfx::Texture *)
#undef CC_MATERIAL_SETPROPERTY_ARRAY_IMPL
const MaterialPropertyVariant *Material::getProperty(const ccstd::string &name, index_t passIdx) const {
if (passIdx == CC_INVALID_INDEX) { // try get property in all possible passes
const auto &propsArray = _props;
size_t len = propsArray.size();
for (size_t i = 0; i < len; i++) {
const auto &props = propsArray[i];
auto iter = props.find(name);
if (iter != props.end()) {
return &iter->second;
}
}
} else {
if (passIdx >= _props.size()) {
CC_LOG_WARNING("illegal pass index: %d.", passIdx);
return nullptr;
}
const auto &passes = *_passes;
const auto &props = _props[passes[passIdx]->getPropertyIndex()];
auto iter = props.find(name);
if (iter != props.end()) {
return &iter->second;
}
}
return nullptr;
}
void Material::fillInfo(const IMaterialInfo &info) {
if (info.technique != ccstd::nullopt) {
_techIdx = info.technique.value();
}
if (info.effectAsset != nullptr) {
_effectAsset = info.effectAsset;
} else if (info.effectName != ccstd::nullopt) {
_effectAsset = EffectAsset::get(info.effectName.value());
}
if (info.defines != ccstd::nullopt) {
prepareInfo(info.defines.value(), _defines);
}
if (info.states != ccstd::nullopt) {
prepareInfo(info.states.value(), _states);
}
}
void Material::copy(const Material *mat, IMaterialInfo *overrides) {
if (mat == nullptr) {
return;
}
_techIdx = mat->_techIdx;
_props.resize(mat->_props.size());
for (size_t i = 0, len = mat->_props.size(); i < len; ++i) {
_props[i] = mat->_props[i];
}
_defines.resize(mat->_defines.size());
for (size_t i = 0, len = mat->_defines.size(); i < len; ++i) {
_defines[i] = mat->_defines[i];
}
_states.resize(mat->_states.size());
for (size_t i = 0, len = mat->_states.size(); i < len; ++i) {
_states[i] = mat->_states[i];
}
_effectAsset = mat->_effectAsset;
if (overrides) {
fillInfo(*overrides);
}
update();
}
void Material::update(bool keepProps /* = true*/) {
if (_effectAsset) {
*_passes = createPasses();
CC_ASSERT(!_effectAsset->_techniques.empty());
// handle property values
size_t totalPasses = _effectAsset->_techniques[_techIdx].passes.size();
_props.resize(totalPasses);
if (keepProps) {
auto cb = [this](auto *pass, size_t i) {
if (i >= _props.size()) {
_props.resize(i + 1);
}
auto &props = _props[i];
if (pass->getPropertyIndex() != CC_INVALID_INDEX) {
props = _props[pass->getPropertyIndex()];
}
for (const auto &prop : props) {
uploadProperty(pass, prop.first, prop.second);
}
};
const auto &passes = *_passes;
for (size_t i = 0, len = passes.size(); i < len; ++i) {
cb(passes[i].get(), i);
}
}
emit<PassesUpdated>();
}
_hash = Material::getHashForMaterial(this);
}
ccstd::vector<IntrusivePtr<scene::Pass>> Material::createPasses() {
ccstd::vector<IntrusivePtr<scene::Pass>> passes;
ITechniqueInfo *tech = nullptr;
if (_techIdx < _effectAsset->_techniques.size()) {
tech = &_effectAsset->_techniques[_techIdx];
}
if (tech == nullptr) {
return passes;
}
size_t passNum = tech->passes.size();
for (size_t k = 0; k < passNum; ++k) {
auto &passInfo = tech->passes[k];
index_t propIdx = passInfo.passIndex = static_cast<index_t>(k);
if (propIdx >= _defines.size()) {
_defines.resize(propIdx + 1);
}
passInfo.defines = _defines[propIdx];
auto &defines = passInfo.defines;
if (propIdx >= _states.size()) {
_states.resize(propIdx + 1);
}
passInfo.stateOverrides = _states[propIdx];
if (passInfo.propertyIndex.has_value()) {
utils::mergeToMap(defines, _defines[passInfo.propertyIndex.value()]);
}
if (passInfo.embeddedMacros.has_value()) {
utils::mergeToMap(defines, passInfo.embeddedMacros.value());
}
if (passInfo.switch_.has_value() && defines.find(passInfo.switch_.value()) == defines.end()) {
continue;
}
auto *pass = ccnew scene::Pass(Root::getInstance());
pass->initialize(passInfo);
passes.emplace_back(pass);
}
return passes;
}
bool Material::uploadProperty(scene::Pass *pass, const ccstd::string &name, const MaterialPropertyVariant &val) {
uint32_t handle = pass->getHandle(name);
if (!handle) {
return false;
}
const auto type = scene::Pass::getTypeFromHandle(handle);
if (type < gfx::Type::SAMPLER1D) {
if (val.index() == MATERIAL_PROPERTY_INDEX_LIST) {
pass->setUniformArray(handle, ccstd::get<MaterialPropertyList>(val));
} else if (val.index() == MATERIAL_PROPERTY_INDEX_SINGLE) {
const auto &passProps = pass->getProperties();
auto iter = passProps.find(name);
if (iter != passProps.end() && iter->second.linear.has_value()) {
CC_ASSERT(ccstd::holds_alternative<MaterialProperty>(val));
const auto &prop = ccstd::get<MaterialProperty>(val);
Vec4 srgb;
if (ccstd::holds_alternative<cc::Color>(prop)) {
srgb = ccstd::get<cc::Color>(prop).toVec4();
} else if (ccstd::holds_alternative<Vec4>(prop)) {
srgb = ccstd::get<Vec4>(prop);
} else {
CC_ABORT();
}
Vec4 linear;
pipeline::srgbToLinear(&linear, srgb);
linear.w = srgb.w;
pass->setUniform(handle, linear);
} else {
pass->setUniform(handle, ccstd::get<MaterialProperty>(val));
}
} else {
pass->resetUniform(name);
}
} else if (val.index() == MATERIAL_PROPERTY_INDEX_LIST) {
const auto &textureArray = ccstd::get<MaterialPropertyList>(val);
for (size_t i = 0; i < textureArray.size(); i++) {
bindTexture(pass, handle, textureArray[i], static_cast<index_t>(i));
}
} else if (val.index() == MATERIAL_PROPERTY_INDEX_SINGLE) {
bindTexture(pass, handle, ccstd::get<MaterialProperty>(val));
} else {
pass->resetTexture(name);
}
return true;
}
void Material::bindTexture(scene::Pass *pass, uint32_t handle, const MaterialProperty &val, uint32_t index) {
if (pass == nullptr) {
return;
}
const uint32_t binding = scene::Pass::getBindingFromHandle(handle);
if (const auto *pTexture = ccstd::get_if<IntrusivePtr<gfx::Texture>>(&val)) {
pass->bindTexture(binding, const_cast<gfx::Texture *>(pTexture->get()), index);
} else if (const auto *pTextureBase = ccstd::get_if<IntrusivePtr<TextureBase>>(&val)) {
auto *textureBase = pTextureBase->get();
gfx::Texture *texture = nullptr;
if (textureBase != nullptr) {
texture = textureBase->getGFXTexture();
}
if (texture == nullptr) {
CC_LOG_WARNING("Material(%p, %s)::bindTexture failed, texture is nullptr", this, _uuid.c_str());
return;
}
if (texture->getWidth() == 0 || texture->getHeight() == 0) {
CC_LOG_WARNING("Material(%p, %s)::bindTexture failed, texture size is 0", this, _uuid.c_str());
return;
}
pass->bindTexture(binding, texture, index);
pass->bindSampler(binding, textureBase->getGFXSampler(), index);
}
}
void Material::initDefault(const ccstd::optional<ccstd::string> &uuid) {
Super::initDefault(uuid);
MacroRecord defines{{"USE_COLOR", true}};
IMaterialInfo info;
info.effectName = ccstd::string{"builtin-unlit"};
info.defines = IMaterialInfo::DefinesType{defines};
initialize(info);
setProperty("mainColor", Color{0xFF, 0x00, 0xFF, 0xFF});
}
bool Material::validate() const {
return _effectAsset != nullptr && !_effectAsset->isDefault() && !_passes->empty();
}
} // namespace cc

View File

@@ -0,0 +1,361 @@
/****************************************************************************
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/Ptr.h"
#include "base/std/container/string.h"
#include "base/std/container/unordered_map.h"
#include "base/std/optional.h"
#include "base/std/variant.h"
#include "core/assets/EffectAsset.h"
#include "core/event/Event.h"
namespace cc {
// class RenderableComponent;
namespace scene {
class Pass;
}
/**
* @en The basic infos for material initialization.
* @zh 用来初始化材质的基本信息。
*/
struct IMaterialInfo {
/**
* @en The EffectAsset to use. Must provide if `effectName` is not specified.
* @zh
* 这个材质将使用的 EffectAsset直接提供资源引用和 `effectName` 至少要指定一个。
*/
EffectAsset *effectAsset{nullptr};
/**
* @en
* The name of the EffectAsset to use. Must provide if `effectAsset` is not specified.
* @zh
* 这个材质将使用的 EffectAsset通过 effect 名指定,和 `effectAsset` 至少要指定一个。
*/
ccstd::optional<ccstd::string> effectName;
/**
* @en
* The index of the technique to use.
* @zh
* 这个材质将使用第几个 technique默认为 0。
*/
ccstd::optional<uint32_t> technique;
using DefinesType = ccstd::variant<ccstd::monostate, MacroRecord, ccstd::vector<MacroRecord>>;
/**
* @en
* The shader macro definitions. Default to 0 or the specified value in [[EffectAsset]].
* @zh
* 这个材质定义的预处理宏,默认全为 0或 [[EffectAsset]] 中的指定值。
*/
ccstd::optional<DefinesType> defines;
using PassOverridesType = ccstd::variant<ccstd::monostate, PassOverrides, ccstd::vector<PassOverrides>>;
/**
* @en
* The override values on top of the pipeline states specified in [[EffectAsset]].
* @zh
* 这个材质的自定义管线状态,将覆盖 effect 中的属性。<br>
* 注意在可能的情况下请尽量少的自定义管线状态,以减小对渲染效率的影响。
*/
ccstd::optional<PassOverridesType> states;
};
class Material : public Asset {
IMPL_EVENT_TARGET(Material)
DECLARE_TARGET_EVENT_BEGIN(Material)
TARGET_EVENT_ARG0(PassesUpdated)
DECLARE_TARGET_EVENT_END()
public:
using Super = Asset;
/**
* @en Get hash for a material
* @zh 获取一个材质的哈希值
* @param material
*/
static ccstd::hash_t getHashForMaterial(Material *material);
Material();
~Material() override;
/**
* @en Initialize this material with the given information.
* @zh 根据所给信息初始化这个材质,初始化正常结束后材质即可立即用于渲染。
* @param info Material description info.
*/
void initialize(const IMaterialInfo &info);
void reset(const IMaterialInfo &info);
void initDefault(const ccstd::optional<ccstd::string> &uuid) override;
bool validate() const override;
/**
* @en
* Destroy the material definitively.<br>
* Cannot re-initialize after destroy.<br>
* Modifications on active materials can be acheived by<br>
* creating a new Material, invoke the `copy` function<br>
* with the desired overrides, and assigning it to the target components.
* @zh
* 彻底销毁材质,注意销毁后无法重新初始化。<br>
* 如需修改现有材质,请创建一个新材质,<br>
* 调用 copy 函数传入需要的 overrides 并赋给目标组件。
*/
bool destroy() override;
/**
* @en Recompile the shader with the specified macro overrides. Allowed only on material instances.
* @zh 使用指定预处理宏重新编译当前 pass数组中的 shader。只允许对材质实例执行。
* @param overrides The shader macro override values.
* @param passIdx The pass to apply to. Will apply to all passes if not specified.
*/
virtual void recompileShaders(const MacroRecord &overrides) {
Material::recompileShaders(overrides, CC_INVALID_INDEX);
}
virtual void recompileShaders(const MacroRecord &overrides, index_t passIdx);
/**
* @en Override the passes with the specified pipeline states. Allowed only on material instances.
* @zh 使用指定管线状态重载当前的 pass数组。只允许对材质实例执行。
* @param overrides The pipeline state override values.
* @param passIdx The pass to apply to. Will apply to all passes if not specified.
*/
virtual void overridePipelineStates(const PassOverrides &overrides) {
Material::overridePipelineStates(overrides, CC_INVALID_INDEX);
}
virtual void overridePipelineStates(const PassOverrides &overrides, index_t passIdx);
/**
* @en Callback function after material is loaded in [[Loader]]. Initialize the resources automatically.
* @zh 通过 [[Loader]] 加载完成时的回调,将自动初始化材质资源。
*/
void onLoaded() override;
/**
* @en Reset all the uniforms to the default value specified in [[EffectAsset]].
* @zh 重置材质的所有 uniform 参数数据为 [[EffectAsset]] 中的默认初始值。
* @param clearPasses Will the rendering data be cleared too?
*/
void resetUniforms(bool clearPasses = true);
/**
* @en
* Convenient property setter provided for quick material setup.<br>
* [[Pass.setUniform]] should be used instead if you need to do per-frame uniform update.
* @zh
* 设置材质 uniform 参数的统一入口。<br>
* 注意如果需要每帧更新 uniform建议使用 [[Pass.setUniform]] 以获得更好的性能。
* @param name The target uniform name.
* @param val The target value.
* @param passIdx The pass to apply to. Will apply to all passes if not specified.
*/
void setProperty(const ccstd::string &name, const MaterialPropertyVariant &val, index_t passIdx = CC_INVALID_INDEX);
void setPropertyNull(const ccstd::string &name, index_t passIdx = CC_INVALID_INDEX);
void setPropertyFloat32(const ccstd::string &name, float val, index_t passIdx = CC_INVALID_INDEX);
void setPropertyInt32(const ccstd::string &name, int32_t val, index_t passIdx = CC_INVALID_INDEX);
void setPropertyVec2(const ccstd::string &name, const Vec2 &val, index_t passIdx = CC_INVALID_INDEX);
void setPropertyVec3(const ccstd::string &name, const Vec3 &val, index_t passIdx = CC_INVALID_INDEX);
void setPropertyVec4(const ccstd::string &name, const Vec4 &val, index_t passIdx = CC_INVALID_INDEX);
void setPropertyColor(const ccstd::string &name, const Color &val, index_t passIdx = CC_INVALID_INDEX);
void setPropertyMat3(const ccstd::string &name, const Mat3 &val, index_t passIdx = CC_INVALID_INDEX);
void setPropertyMat4(const ccstd::string &name, const Mat4 &val, index_t passIdx = CC_INVALID_INDEX);
void setPropertyQuaternion(const ccstd::string &name, const Quaternion &val, index_t passIdx = CC_INVALID_INDEX);
void setPropertyTextureBase(const ccstd::string &name, TextureBase *val, index_t passIdx = CC_INVALID_INDEX);
void setPropertyGFXTexture(const ccstd::string &name, gfx::Texture *val, index_t passIdx = CC_INVALID_INDEX);
void setPropertyFloat32Array(const ccstd::string &name, const ccstd::vector<float> &val, index_t passIdx = CC_INVALID_INDEX);
void setPropertyInt32Array(const ccstd::string &name, const ccstd::vector<int32_t> &val, index_t passIdx = CC_INVALID_INDEX);
void setPropertyVec2Array(const ccstd::string &name, const ccstd::vector<Vec2> &val, index_t passIdx = CC_INVALID_INDEX);
void setPropertyVec3Array(const ccstd::string &name, const ccstd::vector<Vec3> &val, index_t passIdx = CC_INVALID_INDEX);
void setPropertyVec4Array(const ccstd::string &name, const ccstd::vector<Vec4> &val, index_t passIdx = CC_INVALID_INDEX);
void setPropertyColorArray(const ccstd::string &name, const ccstd::vector<cc::Color> &val, index_t passIdx = CC_INVALID_INDEX);
void setPropertyMat3Array(const ccstd::string &name, const ccstd::vector<Mat3> &val, index_t passIdx = CC_INVALID_INDEX);
void setPropertyMat4Array(const ccstd::string &name, const ccstd::vector<Mat4> &val, index_t passIdx = CC_INVALID_INDEX);
void setPropertyQuaternionArray(const ccstd::string &name, const ccstd::vector<Quaternion> &val, index_t passIdx = CC_INVALID_INDEX);
void setPropertyTextureBaseArray(const ccstd::string &name, const ccstd::vector<TextureBase *> &val, index_t passIdx = CC_INVALID_INDEX);
void setPropertyGFXTextureArray(const ccstd::string &name, const ccstd::vector<gfx::Texture *> &val, index_t passIdx = CC_INVALID_INDEX);
/**
* @en
* Get the specified uniform value for this material.<br>
* Note that only uniforms set through [[Material.setProperty]] can be acquired here.<br>
* For the complete rendering data, use [[Pass.getUniform]] instead.
* @zh
* 获取当前材质的指定 uniform 参数的值。<br>
* 注意只有通过 [[Material.setProperty]] 函数设置的参数才能从此函数取出,<br>
* 如需取出完整的渲染数据,请使用 [[Pass.getUniform]]。
* @param name The property or uniform name.
* @param passIdx The target pass index. If not specified, return the first found value in all passes.
*/
const MaterialPropertyVariant *getProperty(const ccstd::string &name, index_t passIdx = CC_INVALID_INDEX) const;
/**
* @en Copy the target material, with optional overrides.
* @zh 复制目标材质到当前实例,允许提供重载信息。。
* @param mat The material to be copied.
* @param overrides The overriding states on top of the original material.
*/
void copy(const Material *mat, IMaterialInfo *overrides = nullptr);
void fillInfo(const IMaterialInfo &info);
// For deserialization, we need to make the following properties public
/* @type(EffectAsset) */
IntrusivePtr<EffectAsset> _effectAsset;
/* @serializable */
uint32_t _techIdx{0};
/* @serializable */
ccstd::vector<MacroRecord> _defines;
/* @serializable */
ccstd::vector<PassOverrides> _states;
/* @serializable */
ccstd::vector<ccstd::unordered_map<ccstd::string, MaterialPropertyVariant>> _props;
//
protected:
std::shared_ptr<ccstd::vector<IntrusivePtr<scene::Pass>>> _passes;
ccstd::hash_t _hash{0U};
public:
/**
* @en Set current [[EffectAsset]].
* @zh 设置使用的 [[EffectAsset]] 资源。
*/
inline void setEffectAsset(EffectAsset *val) {
_effectAsset = val;
}
/**
* @en The current [[EffectAsset]].
* @zh 当前使用的 [[EffectAsset]] 资源。
*/
inline EffectAsset *getEffectAsset() const {
return _effectAsset.get();
}
/**
* @en Name of the current [[EffectAsset]].
* @zh 当前使用的 [[EffectAsset]] 资源名。
*/
inline ccstd::string getEffectName() const {
return _effectAsset ? _effectAsset->getName() : "";
}
/**
* @en The current technique index.
* @zh 当前的 technique 索引。
*/
inline uint32_t getTechniqueIndex() const {
return _techIdx;
}
/**
* @en The passes defined in this material.
* @zh 当前正在使用的 pass 数组。
*/
std::shared_ptr<ccstd::vector<IntrusivePtr<scene::Pass>>> &getPasses() {
return _passes;
}
/**
* @en The hash value of this material.
* @zh 材质的 hash。
*/
inline ccstd::hash_t getHash() const {
return _hash;
}
/**
* @en The parent material
* @zh 父材质
*/
virtual Material *getParent() const {
return nullptr;
}
/**
* @en The owner render component
* @zh 该材质所归属的渲染组件
*/
// virtual RenderableComponent *getOwner() const {
// return nullptr;
// }
protected:
void update(bool keepProps = true);
bool uploadProperty(scene::Pass *pass, const ccstd::string &name, const MaterialPropertyVariant &val);
void bindTexture(scene::Pass *pass, uint32_t handle, const MaterialProperty &val, uint32_t index = 0);
template <typename T1, typename T2>
void prepareInfo(const T1 &patch, ccstd::vector<T2> &cur) {
auto *pOneElement = ccstd::get_if<T2>(&patch);
if (pOneElement != nullptr) {
size_t len = _effectAsset != nullptr ? _effectAsset->_techniques[_techIdx].passes.size() : 1;
ccstd::vector<T2> patchArray;
patchArray.reserve(len);
for (size_t i = 0; i < len; ++i) {
patchArray.emplace_back(*pOneElement);
}
cur.resize(patchArray.size());
for (size_t i = 0; i < len; ++i) {
cur[i] = patchArray[i];
}
} else {
auto *pPatchArray = ccstd::get_if<ccstd::vector<T2>>(&patch);
if (pPatchArray != nullptr) {
const auto &patchArray = *pPatchArray;
size_t len = patchArray.size();
cur.resize(len);
for (size_t i = 0; i < len; ++i) {
cur[i] = patchArray[i];
}
}
}
}
virtual void doDestroy();
virtual ccstd::vector<IntrusivePtr<scene::Pass>> createPasses();
private:
friend class MaterialDeserializer;
CC_DISALLOW_COPY_MOVE_ASSIGN(Material);
};
} // namespace cc

View File

@@ -0,0 +1,175 @@
/****************************************************************************
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 "core/assets/RenderTexture.h"
#include "core/Root.h"
#include "core/platform/Debug.h"
#include "core/utils/IDGenerator.h"
#include "renderer/gfx-base/GFXDef-common.h"
#include "scene/RenderWindow.h"
namespace cc {
namespace {
gfx::RenderPassInfo getDefaultRenderPassInfo(gfx::Device *device) {
gfx::RenderPassInfo info;
info.colorAttachments.push_back({
gfx::Format::RGBA8,
gfx::SampleCount::X1,
gfx::LoadOp::CLEAR,
gfx::StoreOp::STORE,
device->getGeneralBarrier({
gfx::AccessFlagBit::FRAGMENT_SHADER_READ_TEXTURE,
gfx::AccessFlagBit::FRAGMENT_SHADER_READ_TEXTURE,
}),
});
info.depthStencilAttachment.format = gfx::Format::DEPTH_STENCIL;
return info;
}
} // namespace
RenderTexture::RenderTexture() = default;
RenderTexture::~RenderTexture() = default;
void RenderTexture::initialize(const IRenderTextureCreateInfo &info) {
_name = info.name.has_value() ? info.name.value() : "";
_width = info.width;
_height = info.height;
initWindow(info);
}
void RenderTexture::reset(const IRenderTextureCreateInfo &info) {
initialize(info);
}
bool RenderTexture::destroy() {
if (_window != nullptr) {
Root::getInstance()->destroyWindow(_window);
_window = nullptr;
}
return Super::destroy();
}
void RenderTexture::resize(uint32_t width, uint32_t height) {
_width = std::floor(clampf(static_cast<float>(width), 1.F, 2048.F));
_height = std::floor(clampf(static_cast<float>(height), 1.F, 2048.F));
if (_window != nullptr) {
_window->resize(_width, _height);
}
// emit(ccstd::string("resize"), _window); //TODO(xwx): not inherit form Eventify in Asset base class
}
gfx::Texture *RenderTexture::getGFXTexture() const {
return _window ? _window->getFramebuffer()->getColorTextures()[0] : nullptr;
}
void RenderTexture::onLoaded() {
initWindow();
}
void RenderTexture::initWindow() {
auto *device{Root::getInstance()->getDevice()};
cc::scene::IRenderWindowInfo windowInfo;
windowInfo.title = _name;
windowInfo.width = _width;
windowInfo.height = _height;
windowInfo.renderPassInfo = getDefaultRenderPassInfo(device);
if (_window != nullptr) {
_window->destroy();
_window->initialize(device, windowInfo);
} else {
_window = Root::getInstance()->createWindow(windowInfo);
}
}
void RenderTexture::initWindow(const IRenderTextureCreateInfo &info) {
auto *device{Root::getInstance()->getDevice()};
cc::scene::IRenderWindowInfo windowInfo;
windowInfo.title = _name;
windowInfo.width = _width;
windowInfo.height = _height;
if (info.passInfo.has_value()) {
windowInfo.renderPassInfo = info.passInfo.value();
} else {
windowInfo.renderPassInfo = getDefaultRenderPassInfo(device);
}
if (info.externalResLow.has_value()) {
windowInfo.externalResLow = info.externalResLow.value();
}
if (info.externalResHigh.has_value()) {
windowInfo.externalResHigh = info.externalResHigh.value();
}
if (info.externalFlag.has_value()) {
windowInfo.externalFlag = info.externalFlag.value();
}
if (_window != nullptr) {
_window->destroy();
_window->initialize(device, windowInfo);
} else {
_window = Root::getInstance()->createWindow(windowInfo);
}
}
void RenderTexture::initDefault(const ccstd::optional<ccstd::string> &uuid) {
Super::initDefault(uuid);
_width = 1;
_height = 1;
initWindow();
}
bool RenderTexture::validate() const {
return _width >= 1 && _width <= 2048 && _height >= 1 && _height <= 2048;
}
ccstd::vector<uint8_t> RenderTexture::readPixels(uint32_t x, uint32_t y, uint32_t width, uint32_t height) const {
auto *gfxTexture = getGFXTexture();
if (!gfxTexture) {
debug::errorID(7606);
return {};
}
auto *gfxDevice = getGFXDevice();
gfx::BufferTextureCopy region0{};
region0.texOffset.x = static_cast<int32_t>(x);
region0.texOffset.y = static_cast<int32_t>(y);
region0.texExtent.width = width;
region0.texExtent.height = height;
ccstd::vector<uint8_t> buffer;
buffer.resize(width * height * 4);
uint8_t *pBuffer = buffer.data();
gfxDevice->copyTextureToBuffers(gfxTexture, &pBuffer, &region0, 1);
return buffer;
}
} // namespace cc

View File

@@ -0,0 +1,123 @@
/****************************************************************************
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 "core/assets/TextureBase.h"
#include "renderer/gfx-base/GFXDef.h"
namespace cc {
struct IRenderTextureCreateInfo {
ccstd::optional<ccstd::string> name;
uint32_t width;
uint32_t height;
ccstd::optional<gfx::RenderPassInfo> passInfo;
ccstd::optional<uint32_t> externalResLow; // for vulkan vkImage/opengl es texture created from external
ccstd::optional<uint32_t> externalResHigh; // for vulkan vkImage created from external
ccstd::optional<gfx::TextureFlags> externalFlag; // external texture type normal or oes
};
namespace scene {
class RenderWindow;
}
namespace gfx {
class Texture;
class Sampler;
} // namespace gfx
/**
* @en Render texture is a render target for [[Camera]] or [[Canvas]] component,
* the render pipeline will use its [[RenderWindow]] as the target of the rendering process.
* @zh 渲染贴图是 [[Camera]] 或 [[Canvas]] 组件的渲染目标对象,渲染管线会使用它的 [[RenderWindow]] 作为渲染的目标窗口。
*/
class RenderTexture final : public TextureBase {
public:
using Super = TextureBase;
RenderTexture();
~RenderTexture() override;
/**
* @en The render window for the render pipeline, it's created internally and cannot be modified.
* @zh 渲染管线所使用的渲染窗口,内部逻辑创建,无法被修改。
*/
inline scene::RenderWindow *getWindow() const { return _window; }
void initialize(const IRenderTextureCreateInfo &info);
void reset(const IRenderTextureCreateInfo &info); // to be consistent with other assets
bool destroy() override;
/**
* @en Resize the render texture
* @zh 修改渲染贴图的尺寸
* @param width The pixel width, the range is from 1 to 2048
* @param height The pixel height, the range is from 1 to 2048
*/
void resize(uint32_t width, uint32_t height);
// TODO(minggo): migration with TextureBase data
// @ts-expect-error Hack
// get _serialize () { return null; }
// @ts-expect-error Hack
// get _deserialize () { return null; }
// To be compatible with material property interface
/**
* @en Gets the related [[Texture]] resource, it's also the color attachment for the render window
* @zh 获取渲染贴图的 GFX 资源,同时也是渲染窗口所指向的颜色缓冲贴图资源
*/
gfx::Texture *getGFXTexture() const override;
void onLoaded() override;
void initWindow();
void initWindow(const IRenderTextureCreateInfo &info);
void initDefault(const ccstd::optional<ccstd::string> &uuid) override;
bool validate() const override;
/**
* @en Read pixel buffer from render texture
* @param x The location on x axis
* @param y The location on y axis
* @param width The pixel width
* @param height The pixel height
* @zh 从 render texture 读取像素数据
* @param x 起始位置X轴坐标
* @param y 起始位置Y轴坐标
* @param width 像素宽度
* @param height 像素高度
*/
ccstd::vector<uint8_t> readPixels(uint32_t x, uint32_t y, uint32_t width, uint32_t height) const;
private:
scene::RenderWindow *_window{nullptr};
CC_DISALLOW_COPY_MOVE_ASSIGN(RenderTexture);
};
} // namespace cc

View File

@@ -0,0 +1,380 @@
/****************************************************************************
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 "core/assets/RenderingSubMesh.h"
#include <cstdint>
#include "3d/assets/Mesh.h"
#include "3d/misc/Buffer.h"
#include "core/DataView.h"
#include "core/TypedArray.h"
#include "math/Utils.h"
#include "math/Vec3.h"
#include "renderer/gfx-base/GFXBuffer.h"
#include "renderer/gfx-base/GFXDevice.h"
namespace cc {
RenderingSubMesh::RenderingSubMesh(const gfx::BufferList &vertexBuffers,
const gfx::AttributeList &attributes,
gfx::PrimitiveMode primitiveMode)
: RenderingSubMesh(vertexBuffers, attributes, primitiveMode, nullptr, nullptr, true) {
}
RenderingSubMesh::RenderingSubMesh(const gfx::BufferList &vertexBuffers,
const gfx::AttributeList &attributes,
gfx::PrimitiveMode primitiveMode,
gfx::Buffer *indexBuffer)
: RenderingSubMesh(vertexBuffers, attributes, primitiveMode, indexBuffer, nullptr, true) {
}
RenderingSubMesh::RenderingSubMesh(const gfx::BufferList &vertexBuffers,
const gfx::AttributeList &attributes,
gfx::PrimitiveMode primitiveMode,
gfx::Buffer *indexBuffer,
gfx::Buffer *indirectBuffer)
: RenderingSubMesh(vertexBuffers, attributes, primitiveMode, indexBuffer, indirectBuffer, true) {
}
RenderingSubMesh::RenderingSubMesh(const gfx::BufferList &vertexBuffers,
const gfx::AttributeList &attributes,
gfx::PrimitiveMode primitiveMode,
gfx::Buffer *indexBuffer,
gfx::Buffer *indirectBuffer,
bool isOwnerOfIndexBuffer)
: _vertexBuffers(vertexBuffers),
_attributes(attributes),
_primitiveMode(primitiveMode),
_indexBuffer(indexBuffer),
_indirectBuffer(indirectBuffer),
_isOwnerOfIndexBuffer(isOwnerOfIndexBuffer) {
_iaInfo.attributes = attributes;
_iaInfo.vertexBuffers = vertexBuffers;
_iaInfo.indexBuffer = indexBuffer;
_iaInfo.indirectBuffer = indirectBuffer;
}
RenderingSubMesh::~RenderingSubMesh() {
destroy();
}
const IGeometricInfo &RenderingSubMesh::getGeometricInfo() {
if (_geometricInfo.has_value()) {
return _geometricInfo.value();
}
// NOLINTNEXTLINE
static const IGeometricInfo EMPTY_GEOMETRIC_INFO;
if (_mesh == nullptr) {
return EMPTY_GEOMETRIC_INFO;
}
if (!_subMeshIdx.has_value()) {
return EMPTY_GEOMETRIC_INFO;
}
auto iter = std::find_if(_attributes.cbegin(), _attributes.cend(), [](const gfx::Attribute &element) -> bool {
return element.name == gfx::ATTR_NAME_POSITION;
});
if (iter == _attributes.end()) {
return EMPTY_GEOMETRIC_INFO;
}
const auto &attri = *iter;
const uint32_t count = gfx::GFX_FORMAT_INFOS[static_cast<uint32_t>(attri.format)].count;
auto index = static_cast<index_t>(_subMeshIdx.value());
const auto &positionsVar = _mesh->readAttribute(index, gfx::ATTR_NAME_POSITION);
Float32Array const *pPositions = nullptr;
switch (attri.format) {
case gfx::Format::RG32F:
case gfx::Format::RGB32F: {
pPositions = ccstd::get_if<Float32Array>(&positionsVar);
if (pPositions == nullptr) {
return EMPTY_GEOMETRIC_INFO;
}
break;
}
case gfx::Format::RGBA32F: {
const auto *data = ccstd::get_if<Float32Array>(&positionsVar);
if (data == nullptr) {
return EMPTY_GEOMETRIC_INFO;
}
const auto count = data->length() / 4;
auto *pos = ccnew Float32Array(count * 3);
for (uint32_t i = 0; i < count; i++) {
const auto dstPtr = i * 3;
const auto srcPtr = i * 4;
(*pos)[dstPtr] = (*data)[srcPtr];
(*pos)[dstPtr + 1] = (*data)[srcPtr + 1];
(*pos)[dstPtr + 2] = (*data)[srcPtr + 2];
}
pPositions = pos;
break;
}
case gfx::Format::RG16F:
case gfx::Format::RGB16F: {
const auto *data = ccstd::get_if<Uint16Array>(&positionsVar);
if (data == nullptr) {
return EMPTY_GEOMETRIC_INFO;
}
auto *pos = ccnew Float32Array(data->length());
for (uint32_t i = 0; i < data->length(); ++i) {
(*pos)[i] = mathutils::halfToFloat((*data)[i]);
}
pPositions = pos;
break;
}
case gfx::Format::RGBA16F: {
const auto *data = ccstd::get_if<Uint16Array>(&positionsVar);
if (data == nullptr) {
return EMPTY_GEOMETRIC_INFO;
}
const auto count = data->length() / 4;
auto *pos = ccnew Float32Array(count * 3);
for (uint32_t i = 0; i < count; i++) {
const auto dstPtr = i * 3;
const auto srcPtr = i * 4;
(*pos)[dstPtr] = mathutils::halfToFloat((*data)[srcPtr]);
(*pos)[dstPtr + 1] = mathutils::halfToFloat((*data)[srcPtr + 1]);
(*pos)[dstPtr + 2] = mathutils::halfToFloat((*data)[srcPtr + 2]);
}
pPositions = pos;
break;
}
default:
return EMPTY_GEOMETRIC_INFO;
};
const auto &positions = *pPositions;
const auto &indicesVar = _mesh->readIndices(index);
Vec3 max;
Vec3 min;
if (count == 2) {
max.set(positions[0], positions[1], 0);
min.set(positions[0], positions[1], 0);
} else {
max.set(positions[0], positions[1], positions[2]);
min.set(positions[0], positions[1], positions[2]);
}
for (int i = 0; i < positions.length(); i += static_cast<int>(count)) {
if (count == 2) {
max.x = positions[i] > max.x ? positions[i] : max.x;
max.y = positions[i + 1] > max.y ? positions[i + 1] : max.y;
min.x = positions[i] < min.x ? positions[i] : min.x;
min.y = positions[i + 1] < min.y ? positions[i + 1] : min.y;
} else {
max.x = positions[i] > max.x ? positions[i] : max.x;
max.y = positions[i + 1] > max.y ? positions[i + 1] : max.y;
max.z = positions[i + 2] > max.z ? positions[i + 2] : max.z;
min.x = positions[i] < min.x ? positions[i] : min.x;
min.y = positions[i + 1] < min.y ? positions[i + 1] : min.y;
min.z = positions[i + 2] < min.z ? positions[i + 2] : min.z;
}
}
IGeometricInfo info;
info.positions = positions;
info.indices = indicesVar;
info.boundingBox.max = max;
info.boundingBox.min = min;
_geometricInfo = info;
return _geometricInfo.value();
}
void RenderingSubMesh::genFlatBuffers() {
if (!_flatBuffers.empty() || _mesh == nullptr || !_subMeshIdx.has_value()) {
return;
}
uint32_t idxCount = 0;
const auto &prim = _mesh->getStruct().primitives[_subMeshIdx.value()];
if (prim.indexView.has_value()) {
idxCount = prim.indexView.value().count;
}
for (size_t i = 0; i < prim.vertexBundelIndices.size(); i++) {
const uint32_t bundleIdx = prim.vertexBundelIndices[i];
const Mesh::IVertexBundle &vertexBundle = _mesh->getStruct().vertexBundles[bundleIdx];
const uint32_t vbCount = prim.indexView.has_value() ? prim.indexView.value().count : vertexBundle.view.count;
const uint32_t vbStride = vertexBundle.view.stride;
const uint32_t vbSize = vbStride * vbCount;
Uint8Array view(_mesh->getData().buffer(), vertexBundle.view.offset, vertexBundle.view.length);
Uint8Array sharedView(prim.indexView.has_value() ? vbSize : vertexBundle.view.length);
if (!prim.indexView.has_value()) {
sharedView.set(_mesh->getData().subarray(vertexBundle.view.offset, vertexBundle.view.offset + vertexBundle.view.length));
_flatBuffers.emplace_back(IFlatBuffer{vbStride, vbCount, sharedView});
continue;
}
IBArray ibView = _mesh->readIndices(static_cast<int>(_subMeshIdx.value()));
// transform to flat buffer
for (uint32_t n = 0; n < idxCount; ++n) {
auto idx = getIBArrayValue<int32_t>(ibView, static_cast<int>(n));
uint32_t offset = n * vbStride;
uint32_t srcOffset = idx * vbStride;
for (uint32_t m = 0; m < vbStride; ++m) {
sharedView[static_cast<int>(offset + m)] = view[static_cast<int>(srcOffset + m)];
}
}
_flatBuffers.emplace_back(IFlatBuffer{vbStride, vbCount, std::move(sharedView)});
}
}
void RenderingSubMesh::enableVertexIdChannel(gfx::Device *device) {
if (_vertexIdChannel.has_value()) {
return;
}
const auto streamIndex = static_cast<uint32_t>(_vertexBuffers.size());
const auto attributeIndex = static_cast<uint32_t>(_attributes.size());
gfx::Buffer *vertexIdBuffer = allocVertexIdBuffer(device);
_vertexBuffers.pushBack(vertexIdBuffer);
_attributes.push_back({"a_vertexId", gfx::Format::R32F, false, streamIndex, false, 0});
_iaInfo.attributes = _attributes;
_iaInfo.vertexBuffers = _vertexBuffers.get();
_vertexIdChannel = VertexIdChannel{
streamIndex,
attributeIndex,
};
}
bool RenderingSubMesh::destroy() {
_vertexBuffers.clear();
_indexBuffer = nullptr;
_indirectBuffer = nullptr;
if (!_jointMappedBuffers.empty() && !_jointMappedBufferIndices.empty()) {
for (uint32_t index : _jointMappedBufferIndices) {
_jointMappedBuffers.at(index)->destroy();
}
_jointMappedBuffers.clear();
_jointMappedBufferIndices.clear();
}
return true;
}
const gfx::BufferList &RenderingSubMesh::getJointMappedBuffers() {
if (!_jointMappedBuffers.empty()) {
return _jointMappedBuffers.get();
}
auto &buffers = _jointMappedBuffers;
auto &indices = _jointMappedBufferIndices;
if (!_mesh || !_subMeshIdx.has_value()) {
_jointMappedBuffers = _vertexBuffers;
return _jointMappedBuffers.get();
}
const auto &structInfo = _mesh->getStruct();
const auto &prim = structInfo.primitives[_subMeshIdx.value()];
if (!structInfo.jointMaps.has_value() || !prim.jointMapIndex.has_value() || structInfo.jointMaps.value()[prim.jointMapIndex.value()].empty()) {
_jointMappedBuffers = _vertexBuffers;
return _jointMappedBuffers.get();
}
gfx::Format jointFormat = gfx::Format::UNKNOWN;
int32_t jointOffset = 0;
gfx::Device *device = gfx::Device::getInstance();
for (size_t i = 0; i < prim.vertexBundelIndices.size(); i++) {
const auto &bundle = structInfo.vertexBundles[prim.vertexBundelIndices[i]];
jointOffset = 0;
jointFormat = gfx::Format::UNKNOWN;
for (const auto &attr : bundle.attributes) {
if (attr.name == gfx::ATTR_NAME_JOINTS) {
jointFormat = attr.format;
break;
}
jointOffset += static_cast<int32_t>(gfx::GFX_FORMAT_INFOS[static_cast<int32_t>(attr.format)].size);
}
if (jointFormat != gfx::Format::UNKNOWN) {
Uint8Array data{_mesh->getData().buffer(), bundle.view.offset, bundle.view.length};
DataView dataView(data.slice().buffer());
const auto &idxMap = structInfo.jointMaps.value()[prim.jointMapIndex.value()];
mapBuffer(
dataView, [&](const DataVariant &cur, uint32_t /*idx*/, const DataView & /*view*/) -> DataVariant {
if (ccstd::holds_alternative<int32_t>(cur)) {
auto iter = std::find(idxMap.begin(), idxMap.end(), ccstd::get<int32_t>(cur));
if (iter != idxMap.end()) {
return static_cast<int32_t>(iter - idxMap.begin());
}
}
CC_ABORT();
return -1;
},
jointFormat, jointOffset, bundle.view.length, bundle.view.stride, &dataView);
auto *buffer = device->createBuffer(gfx::BufferInfo{
gfx::BufferUsageBit::VERTEX | gfx::BufferUsageBit::TRANSFER_DST,
gfx::MemoryUsageBit::DEVICE,
bundle.view.length,
bundle.view.stride});
buffer->update(dataView.buffer()->getData());
buffers.pushBack(buffer);
indices.emplace_back(i);
} else {
buffers.pushBack(_vertexBuffers.at(prim.vertexBundelIndices[i]));
}
}
if (_vertexIdChannel) {
buffers.pushBack(allocVertexIdBuffer(device));
}
return buffers.get();
}
gfx::Buffer *RenderingSubMesh::allocVertexIdBuffer(gfx::Device *device) {
const uint32_t vertexCount = (_vertexBuffers.empty() || _vertexBuffers.at(0)->getStride() == 0)
? 0
// TODO(minggo): This depends on how stride of a vertex buffer is defined; Consider padding problem.
: _vertexBuffers.at(0)->getSize() / _vertexBuffers.at(0)->getStride();
ccstd::vector<float> vertexIds(vertexCount);
for (int iVertex = 0; iVertex < vertexCount; ++iVertex) {
// `+0.5` because on some platforms, the "fetched integer" may have small error.
// For example `26` may yield `25.99999`, which is convert to `25` instead of `26` using `int()`.
vertexIds[iVertex] = static_cast<float>(iVertex) + 0.5F;
}
uint32_t vertexIdxByteLength = sizeof(float) * vertexCount;
gfx::Buffer *vertexIdBuffer = device->createBuffer({
gfx::BufferUsageBit::VERTEX | gfx::BufferUsageBit::TRANSFER_DST,
gfx::MemoryUsageBit::DEVICE,
vertexIdxByteLength,
sizeof(float),
});
vertexIdBuffer->update(vertexIds.data(), vertexIdxByteLength);
return vertexIdBuffer;
}
} // namespace cc

View File

@@ -0,0 +1,230 @@
/****************************************************************************
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 "3d/assets/Types.h"
#include "base/RefCounted.h"
#include "base/RefVector.h"
#include "base/std/variant.h"
#include "core/TypedArray.h"
#include "core/Types.h"
#include "renderer/gfx-base/GFXDef.h"
namespace cc {
class Mesh;
/**
* @en The interface of geometric information
* @zh 几何信息。
*/
struct IGeometricInfo {
/**
* @en Vertex positions
* @zh 顶点位置。
*/
Float32Array positions;
/**
* @en Indices data
* @zh 索引数据。
*/
ccstd::optional<IBArray> indices;
/**
* @en Whether the geometry is treated as double sided
* @zh 是否将图元按双面对待。
*/
ccstd::optional<bool> doubleSided;
/**
* @en The bounding box
* @zh 此几何体的轴对齐包围盒。
*/
BoundingBox boundingBox;
};
/**
* @en Flat vertex buffer
* @zh 扁平化顶点缓冲区
*/
struct IFlatBuffer {
uint32_t stride{0};
uint32_t count{0};
Uint8Array buffer;
};
namespace gfx {
class Buffer;
}
/**
* @en Sub mesh for rendering which contains all geometry data, it can be used to create [[InputAssembler]].
* @zh 包含所有顶点数据的渲染子网格,可以用来创建 [[InputAssembler]]。
*/
class RenderingSubMesh : public RefCounted {
public:
RenderingSubMesh(const gfx::BufferList &vertexBuffers,
const gfx::AttributeList &attributes,
gfx::PrimitiveMode primitiveMode);
RenderingSubMesh(const gfx::BufferList &vertexBuffers,
const gfx::AttributeList &attributes,
gfx::PrimitiveMode primitiveMode,
gfx::Buffer *indexBuffer);
RenderingSubMesh(const gfx::BufferList &vertexBuffers,
const gfx::AttributeList &attributes,
gfx::PrimitiveMode primitiveMode,
gfx::Buffer *indexBuffer,
gfx::Buffer *indirectBuffer);
RenderingSubMesh(const gfx::BufferList &vertexBuffers,
const gfx::AttributeList &attributes,
gfx::PrimitiveMode primitiveMode,
gfx::Buffer *indexBuffer,
gfx::Buffer *indirectBuffer,
bool isOwnerOfIndexBuffer);
~RenderingSubMesh() override;
/**
* @en All vertex attributes used by the sub mesh
* @zh 所有顶点属性。
*/
inline const gfx::AttributeList &getAttributes() const { return _attributes; }
/**
* @en All vertex buffers used by the sub mesh
* @zh 使用的所有顶点缓冲区。
*/
inline const gfx::BufferList &getVertexBuffers() const { return _vertexBuffers.get(); }
/**
* @en Index buffer used by the sub mesh
* @zh 使用的索引缓冲区,若未使用则无需指定。
*/
inline gfx::Buffer *getIndexBuffer() const { return _indexBuffer; }
/**
* @en Indirect buffer used by the sub mesh
* @zh 间接绘制缓冲区。
*/
inline gfx::Buffer *indirectBuffer() const { return _indirectBuffer; }
/**
* @en The geometric info of the sub mesh, used for raycast.
* @zh (用于射线检测的)几何信息。
*/
const IGeometricInfo &getGeometricInfo();
/**
* @en Invalidate the geometric info of the sub mesh after geometry changed.
* @zh 网格更新后,设置(用于射线检测的)几何信息为无效,需要重新计算。
*/
inline void invalidateGeometricInfo() { _geometricInfo.reset(); }
/**
* @en Primitive mode used by the sub mesh
* @zh 图元类型。
*/
inline gfx::PrimitiveMode getPrimitiveMode() const { return _primitiveMode; }
/**
* @en Flatted vertex buffers
* @zh 扁平化的顶点缓冲区。
*/
inline const ccstd::vector<IFlatBuffer> &getFlatBuffers() const { return _flatBuffers; }
inline void setFlatBuffers(const ccstd::vector<IFlatBuffer> &flatBuffers) { _flatBuffers = flatBuffers; }
void genFlatBuffers();
inline const gfx::InputAssemblerInfo &getIaInfo() const { return _iaInfo; }
inline gfx::InputAssemblerInfo &getIaInfo() { return _iaInfo; }
inline void setDrawInfo(const gfx::DrawInfo &info) { _drawInfo = info; }
inline ccstd::optional<gfx::DrawInfo> &getDrawInfo() { return _drawInfo; }
/**
* @en The vertex buffer for joint after mapping
* @zh 骨骼索引按映射表处理后的顶点缓冲。
*/
const gfx::BufferList &getJointMappedBuffers();
bool destroy();
/**
* @en Adds a vertex attribute input called 'a_vertexId' into this sub-mesh.
* This is useful if you want to simulate `gl_VertexId` in WebGL context prior to 2.0.
* Once you call this function, the vertex attribute is permanently added.
* Subsequent calls to this function take no effect.
* @param device Device used to create related rendering resources.
*/
void enableVertexIdChannel(gfx::Device *device);
inline void setMesh(Mesh *mesh) { _mesh = mesh; }
inline Mesh *getMesh() const { return _mesh; }
inline void setSubMeshIdx(const ccstd::optional<uint32_t> &idx) { _subMeshIdx = idx; }
inline const ccstd::optional<uint32_t> &getSubMeshIdx() const { return _subMeshIdx; }
private:
gfx::Buffer *allocVertexIdBuffer(gfx::Device *device);
bool _isOwnerOfIndexBuffer{true};
// Mesh will includes RenderingSubMesh, so use Mesh* here.
Mesh *_mesh{nullptr};
ccstd::optional<uint32_t> _subMeshIdx;
ccstd::vector<IFlatBuffer> _flatBuffers;
// As gfx::InputAssemblerInfo needs the data structure, so not use IntrusivePtr.
RefVector<gfx::Buffer *> _jointMappedBuffers;
ccstd::vector<uint32_t> _jointMappedBufferIndices;
ccstd::optional<VertexIdChannel> _vertexIdChannel;
ccstd::optional<IGeometricInfo> _geometricInfo;
// As gfx::InputAssemblerInfo needs the data structure, so not use IntrusivePtr.
RefVector<gfx::Buffer *> _vertexBuffers;
gfx::AttributeList _attributes;
IntrusivePtr<gfx::Buffer> _indexBuffer;
IntrusivePtr<gfx::Buffer> _indirectBuffer;
gfx::PrimitiveMode _primitiveMode{gfx::PrimitiveMode::TRIANGLE_LIST};
gfx::InputAssemblerInfo _iaInfo;
ccstd::optional<gfx::DrawInfo> _drawInfo;
CC_DISALLOW_COPY_MOVE_ASSIGN(RenderingSubMesh);
};
} // namespace cc

View File

@@ -0,0 +1,45 @@
/****************************************************************************
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 "core/assets/SceneAsset.h"
#include "base/memory/Memory.h"
#include "core/scene-graph/Scene.h"
namespace cc {
SceneAsset::SceneAsset() = default;
SceneAsset::~SceneAsset() = default;
bool SceneAsset::validate() const {
return _scene.get() != nullptr;
}
void SceneAsset::setScene(Scene *scene) { _scene = scene; };
void SceneAsset::initDefault(const ccstd::optional<ccstd::string> &uuid) {
Super::initDefault(uuid);
_scene = ccnew Scene("New Scene");
}
} // namespace cc

View File

@@ -0,0 +1,59 @@
/****************************************************************************
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/Ptr.h"
#include "core/assets/Asset.h"
namespace cc {
class Scene;
class SceneAsset final : public Asset {
public:
using Super = Asset;
SceneAsset();
~SceneAsset() override;
void initDefault(const ccstd::optional<ccstd::string> &uuid) override;
bool validate() const override;
inline Scene *getScene() const { return _scene.get(); }
void setScene(Scene *scene);
private:
/**
* @en The scene node
* @zh 场景节点。
@editable
@serializable*/
IntrusivePtr<Scene> _scene;
CC_DISALLOW_COPY_MOVE_ASSIGN(SceneAsset);
};
} // namespace cc

View File

@@ -0,0 +1,233 @@
/****************************************************************************
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 "core/assets/SimpleTexture.h"
#include "core/assets/ImageAsset.h"
#include "core/platform/Debug.h"
#include "core/platform/Macro.h"
#include "renderer/gfx-base/GFXDevice.h"
namespace cc {
namespace {
uint32_t getMipLevel(uint32_t width, uint32_t height) {
uint32_t size = std::max(width, height);
uint32_t level = 0;
while (size) {
size >>= 1;
level++;
}
return level;
}
bool isPOT(uint32_t n) { return n && (n & (n - 1)) == 0; }
bool canGenerateMipmap(uint32_t w, uint32_t h) {
return isPOT(w) && isPOT(h);
}
} // namespace
SimpleTexture::SimpleTexture() = default;
SimpleTexture::~SimpleTexture() = default;
bool SimpleTexture::destroy() {
tryDestroyTextureView();
tryDestroyTexture();
return Super::destroy();
}
void SimpleTexture::updateImage() {
updateMipmaps(0, 0);
}
void SimpleTexture::uploadDataWithArrayBuffer(const ArrayBuffer &source, uint32_t level /* = 0 */, uint32_t arrayIndex /* = 0 */) {
uploadData(source.getData(), level, arrayIndex);
}
void SimpleTexture::uploadData(const uint8_t *source, uint32_t level /* = 0 */, uint32_t arrayIndex /* = 0 */) {
if (!_gfxTexture || _mipmapLevel <= level) {
return;
}
auto *gfxDevice = getGFXDevice();
if (!gfxDevice) {
return;
}
gfx::BufferTextureCopy region;
region.texExtent.width = _textureWidth >> level;
region.texExtent.height = _textureHeight >> level;
region.texSubres.mipLevel = level;
region.texSubres.baseArrayLayer = arrayIndex;
const uint8_t *buffers[1]{source};
gfxDevice->copyBuffersToTexture(buffers, _gfxTexture, &region, 1);
}
void SimpleTexture::assignImage(ImageAsset *image, uint32_t level, uint32_t arrayIndex /* = 0 */) {
const uint8_t *data = image->getData();
if (!data) {
return;
}
uploadData(data, level, arrayIndex);
checkTextureLoaded();
emit<AfterAssignImage>(image);
}
void SimpleTexture::checkTextureLoaded() {
textureReady();
}
void SimpleTexture::textureReady() {
_loaded = true;
//cjh this.emit('load');
}
void SimpleTexture::setMipmapLevel(uint32_t value) {
_mipmapLevel = value < 1 ? 1 : value;
}
void SimpleTexture::tryReset() {
tryDestroyTextureView();
tryDestroyTexture();
if (_mipmapLevel == 0) {
return;
}
auto *device = getGFXDevice();
if (!device) {
return;
}
createTexture(device);
_gfxTextureView = createTextureView(device);
}
void SimpleTexture::createTexture(gfx::Device *device) {
if (_width == 0 || _height == 0) {
return;
}
auto flags = gfx::TextureFlagBit::NONE;
auto usage = gfx::TextureUsageBit::SAMPLED | gfx::TextureUsageBit::TRANSFER_DST;
if (_mipFilter != Filter::NONE && canGenerateMipmap(_width, _height)) {
_mipmapLevel = getMipLevel(_width, _height);
if (!isUsingOfflineMipmaps() && !isCompressed()) {
flags = gfx::TextureFlagBit::GEN_MIPMAP;
}
}
const auto gfxFormat = getGFXFormat();
if (hasFlag(gfx::Device::getInstance()->getFormatFeatures(gfxFormat), gfx::FormatFeatureBit::RENDER_TARGET)) {
usage |= gfx::TextureUsageBit::COLOR_ATTACHMENT;
}
auto textureCreateInfo = getGfxTextureCreateInfo(
usage,
gfxFormat,
_mipmapLevel,
flags);
//cjh if (!textureCreateInfo) {
// return;
// }
auto *texture = device->createTexture(textureCreateInfo);
_textureWidth = textureCreateInfo.width;
_textureHeight = textureCreateInfo.height;
_gfxTexture = texture;
notifyTextureUpdated();
}
gfx::Texture *SimpleTexture::createTextureView(gfx::Device *device) {
if (!_gfxTexture) {
return nullptr;
}
const uint32_t maxLevel = _maxLevel < _mipmapLevel ? _maxLevel : _mipmapLevel - 1;
auto textureViewCreateInfo = getGfxTextureViewCreateInfo(
_gfxTexture,
getGFXFormat(),
_baseLevel,
maxLevel - _baseLevel + 1);
//TODO(minggo)
// if (!textureViewCreateInfo) {
// return;
// }
return device->createTexture(textureViewCreateInfo);
}
void SimpleTexture::tryDestroyTexture() {
if (_gfxTexture != nullptr) {
_gfxTexture->destroy();
_gfxTexture = nullptr;
notifyTextureUpdated();
}
}
void SimpleTexture::tryDestroyTextureView() {
if (_gfxTextureView != nullptr) {
_gfxTextureView->destroy();
_gfxTextureView = nullptr;
//TODO(minggo): should notify JS if the performance is low.
}
}
void SimpleTexture::setMipRange(uint32_t baseLevel, uint32_t maxLevel) {
debug::assertID(baseLevel <= maxLevel, 3124);
setMipRangeInternal(baseLevel, maxLevel);
auto *device = getGFXDevice();
if (!device) {
return;
}
// create a new texture view before the destruction of the previous one to bypass the bug that
// vulkan destroys textureview in use. This is a temporary solution, should be fixed later.
gfx::Texture *textureView = createTextureView(device);
tryDestroyTextureView();
_gfxTextureView = textureView;
}
bool SimpleTexture::isUsingOfflineMipmaps() {
return false;
}
void SimpleTexture::setMipRangeInternal(uint32_t baseLevel, uint32_t maxLevel) {
_baseLevel = baseLevel < 1 ? 0 : baseLevel;
_maxLevel = _maxLevel < 1 ? 0 : maxLevel;
}
void SimpleTexture::notifyTextureUpdated() {
emit<TextureUpdated>(_gfxTexture.get());
}
} // namespace cc

View File

@@ -0,0 +1,171 @@
/****************************************************************************
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 "core/ArrayBuffer.h"
#include "core/assets/TextureBase.h"
namespace cc {
class ImageAsset;
/**
* @en The simple texture base class.
* It create the GFX Texture and can set mipmap levels.
* @zh 简单贴图基类。
* 简单贴图内部创建了 GFX 贴图和该贴图上的 GFX 贴图视图。
* 简单贴图允许指定不同的 Mipmap 层级。
*/
class SimpleTexture : public TextureBase {
IMPL_EVENT_TARGET(SimpleTexture)
DECLARE_TARGET_EVENT_BEGIN_WITH_PARENTS(SimpleTexture, TextureBase)
TARGET_EVENT_ARG1(TextureUpdated, cc::gfx::Texture *)
TARGET_EVENT_ARG1(AfterAssignImage, cc::ImageAsset *)
DECLARE_TARGET_EVENT_END()
public:
~SimpleTexture() override;
using Super = TextureBase;
/**
* @en The mipmap level of the texture
* @zh 贴图中的 Mipmap 层级数量
*/
inline uint32_t mipmapLevel() const {
return _mipmapLevel;
}
/**
* @en The GFX Texture resource
* @zh 获取此贴图底层的 GFX 贴图对象。
*/
gfx::Texture *getGFXTexture() const override {
return _gfxTextureView.get();
}
bool destroy() override;
/**
* @en Update the level 0 mipmap image.
* @zh 更新 0 级 Mipmap。
*/
void updateImage();
/**
* @en Update the given level mipmap image.
* @zh 更新指定层级范围内的 Mipmap。当 Mipmap 数据发生了改变时应调用此方法提交更改。
* 若指定的层级范围超出了实际已有的层级范围,只有覆盖的那些层级范围会被更新。
* @param firstLevel First level to be updated
* @param count Mipmap level count to be updated
*/
virtual void updateMipmaps(uint32_t firstLevel, uint32_t count) {}
/**
* @en Upload data to the given mipmap level.
* The size of the image will affect how the mipmap is updated.
* - When the image is an ArrayBuffer, the size of the image must match the mipmap size.
* - If the image size matches the mipmap size, the mipmap data will be updated entirely.
* - If the image size is smaller than the mipmap size, the mipmap will be updated from top left corner.
* - If the image size is larger, an error will be raised
* @zh 上传图像数据到指定层级的 Mipmap 中。
* 图像的尺寸影响 Mipmap 的更新范围:
* - 当图像是 `ArrayBuffer` 时,图像的尺寸必须和 Mipmap 的尺寸一致;否则,
* - 若图像的尺寸与 Mipmap 的尺寸相同,上传后整个 Mipmap 的数据将与图像数据一致;
* - 若图像的尺寸小于指定层级 Mipmap 的尺寸(不管是长或宽),则从贴图左上角开始,图像尺寸范围内的 Mipmap 会被更新;
* - 若图像的尺寸超出了指定层级 Mipmap 的尺寸(不管是长或宽),都将引起错误。
* @param source The source image or image data
* @param level Mipmap level to upload the image to
* @param arrayIndex The array index
*/
void uploadDataWithArrayBuffer(const ArrayBuffer &source, uint32_t level = 0, uint32_t arrayIndex = 0);
void uploadData(const uint8_t *source, uint32_t level = 0, uint32_t arrayIndex = 0);
void assignImage(ImageAsset *image, uint32_t level, uint32_t arrayIndex = 0);
void checkTextureLoaded();
/**
* Set mipmap level of this texture.
* The value is passes as presumed info to `this._getGfxTextureCreateInfo()`.
* @param value The mipmap level.
* @warn As it is invoked by subclass(TextureCube) in TS, so should make it as public.
*/
void setMipmapLevel(uint32_t value);
/**
* Set mipmap level range for this texture.
* @param baseLevel The base mipmap level.
* @param maxLevel The maximum mipmap level.
*/
void setMipRange(uint32_t baseLevel, uint32_t maxLevel);
/**
* @en Whether mipmaps are baked convolutional maps.
* @zh mipmaps是否为烘焙出来的卷积图。
*/
virtual bool isUsingOfflineMipmaps();
protected:
SimpleTexture();
void textureReady();
/**
* @en This method is override by derived classes to provide GFX texture info.
* @zh 这个方法被派生类重写以提供 GFX 纹理信息。
* @param presumed The presumed GFX texture info.
*/
virtual gfx::TextureInfo getGfxTextureCreateInfo(gfx::TextureUsageBit usage, gfx::Format format, uint32_t levelCount, gfx::TextureFlagBit flags) = 0;
/**
* @en This method is overrided by derived classes to provide GFX TextureViewInfo.
* @zh 这个方法被派生类重写以提供 GFX 纹理视图信息。
* @param presumed The presumed GFX TextureViewInfo.
*/
virtual gfx::TextureViewInfo getGfxTextureViewCreateInfo(gfx::Texture *texture, gfx::Format format, uint32_t baseLevel, uint32_t levelCount) = 0;
void tryReset();
void createTexture(gfx::Device *device);
gfx::Texture *createTextureView(gfx::Device *device);
void tryDestroyTexture();
void tryDestroyTextureView();
void notifyTextureUpdated();
void setMipRangeInternal(uint32_t baseLevel, uint32_t maxLevel);
IntrusivePtr<gfx::Texture> _gfxTexture;
IntrusivePtr<gfx::Texture> _gfxTextureView;
uint32_t _mipmapLevel{1};
// Cache these data to reduce JSB invoking.
uint32_t _textureWidth{0};
uint32_t _textureHeight{0};
uint32_t _baseLevel{0};
uint32_t _maxLevel{1000};
CC_DISALLOW_COPY_MOVE_ASSIGN(SimpleTexture);
};
} // namespace cc

View File

@@ -0,0 +1,55 @@
/****************************************************************************
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 "core/assets/Asset.h"
namespace cc {
/**
* @en Class for text file.
* @zh 文本资源。
*/
class TextAsset final : public Asset {
public:
explicit TextAsset() = default;
~TextAsset() override = default;
/**
* @en The text content.
* @zh 此资源包含的文本。
@serializable
@editable*/
ccstd::string text;
ccstd::string toString() const override {
return text;
}
private:
CC_DISALLOW_COPY_MOVE_ASSIGN(TextAsset);
};
} // namespace cc

View File

@@ -0,0 +1,242 @@
/****************************************************************************
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 "core/assets/Texture2D.h"
#include <sstream>
#include "base/Log.h"
#include "core/assets/ImageAsset.h"
namespace cc {
Texture2D::Texture2D() = default;
Texture2D::~Texture2D() = default;
void Texture2D::syncMipmapsForJS(const ccstd::vector<IntrusivePtr<ImageAsset>> &value) {
_mipmaps = value;
}
void Texture2D::setMipmaps(const ccstd::vector<IntrusivePtr<ImageAsset>> &value) {
_mipmaps = value;
auto mipmaps = ccstd::vector<IntrusivePtr<ImageAsset>>{};
if (value.size() == 1) {
const auto images = value[0]->extractMipmaps();
std::copy(std::cbegin(images), std::cend(images), std::back_inserter(mipmaps));
} else if (value.size() > 1) {
for (const auto &image : value) {
mipmaps.emplace_back(image->extractMipmap0());
}
}
setMipmapParams(mipmaps);
}
void Texture2D::setMipmapParams(const ccstd::vector<IntrusivePtr<ImageAsset>> &value) {
_generatedMipmaps = value;
setMipmapLevel(static_cast<uint32_t>(_generatedMipmaps.size()));
if (!_generatedMipmaps.empty()) {
ImageAsset *imageAsset = _generatedMipmaps[0];
ITexture2DCreateInfo info;
info.width = imageAsset->getWidth();
info.height = imageAsset->getHeight();
info.format = imageAsset->getFormat();
info.mipmapLevel = static_cast<uint32_t>(_generatedMipmaps.size());
info.baseLevel = _baseLevel;
info.maxLevel = _maxLevel;
reset(info);
for (size_t i = 0, len = _generatedMipmaps.size(); i < len; ++i) {
assignImage(_generatedMipmaps[i], static_cast<uint32_t>(i));
}
} else {
ITexture2DCreateInfo info;
info.width = 0;
info.height = 0;
info.mipmapLevel = static_cast<uint32_t>(_generatedMipmaps.size());
info.baseLevel = _baseLevel;
info.maxLevel = _maxLevel;
reset(info);
}
}
void Texture2D::initialize() {
setMipmaps(_mipmaps);
}
void Texture2D::onLoaded() {
initialize();
}
void Texture2D::reset(const ITexture2DCreateInfo &info) {
_width = info.width;
_height = info.height;
setGFXFormat(info.format);
const uint32_t mipLevels = info.mipmapLevel.has_value() ? info.mipmapLevel.value() : 1;
setMipmapLevel(mipLevels);
const uint32_t minLod = info.baseLevel.has_value() ? info.baseLevel.value() : 0;
const uint32_t maxLod = info.maxLevel.has_value() ? info.maxLevel.value() : 1000;
setMipRange(minLod, maxLod);
tryReset();
}
void Texture2D::create(uint32_t width, uint32_t height, PixelFormat format /* = PixelFormat::RGBA8888*/, uint32_t mipmapLevel /* = 1*/, uint32_t baseLevel, uint32_t maxLevel) {
reset({width,
height,
format,
mipmapLevel,
baseLevel,
maxLevel});
}
ccstd::string Texture2D::toString() const {
ccstd::string ret;
if (!_mipmaps.empty()) {
ret = _mipmaps[0]->getUrl();
}
return ret;
}
void Texture2D::updateMipmaps(uint32_t firstLevel, uint32_t count) {
if (firstLevel >= _generatedMipmaps.size()) {
return;
}
const auto nUpdate = static_cast<uint32_t>(std::min(
(count == 0 ? _generatedMipmaps.size() : count),
(_generatedMipmaps.size() - firstLevel)));
for (uint32_t i = 0; i < nUpdate; ++i) {
const uint32_t level = firstLevel + i;
assignImage(_generatedMipmaps[level], level);
}
}
bool Texture2D::destroy() {
_mipmaps.clear();
_generatedMipmaps.clear();
return Super::destroy();
}
ccstd::string Texture2D::description() const {
std::stringstream ret;
ccstd::string url;
if (!_mipmaps.empty()) {
url = _mipmaps[0]->getUrl();
}
ret << "<cc.Texture2D | Name = " << url << " | Dimension" << _width << " x " << _height << ">";
return ret.str();
}
void Texture2D::releaseTexture() {
destroy();
}
ccstd::any Texture2D::serialize(const ccstd::any & /*ctxForExporting*/) {
// if (EDITOR || TEST) {
// return {
// base: super._serialize(ctxForExporting),
// mipmaps: this._mipmaps.map((mipmap) => {
// if (!mipmap || !mipmap._uuid) {
// return null;
// }
// if (ctxForExporting && ctxForExporting._compressUuid) {
// // ctxForExporting.dependsOn('_textureSource', texture); TODO
// return EditorExtends.UuidUtils.compressUuid(mipmap._uuid, true);
// }
// return mipmap._uuid;
// }),
// };
// }
return nullptr;
}
void Texture2D::deserialize(const ccstd::any &serializedData, const ccstd::any &handle) {
const auto *data = ccstd::any_cast<ITexture2DSerializeData>(&serializedData);
if (data == nullptr) {
CC_LOG_WARNING("serializedData is not ITexture2DSerializeData");
return;
}
Super::deserialize(data->base, handle);
_mipmaps.resize(data->mipmaps.size());
for (size_t i = 0; i < data->mipmaps.size(); ++i) {
// Prevent resource load failed
_mipmaps[i] = ccnew ImageAsset();
if (data->mipmaps[i].empty()) {
continue;
}
ccstd::string mipmapUUID = data->mipmaps[i];
//cjh TODO: handle.result.push(this._mipmaps, `${i}`, mipmapUUID, js.getClassId(ImageAsset));
}
}
gfx::TextureInfo Texture2D::getGfxTextureCreateInfo(gfx::TextureUsageBit usage, gfx::Format format, uint32_t levelCount, gfx::TextureFlagBit flags) {
gfx::TextureInfo texInfo;
texInfo.type = gfx::TextureType::TEX2D;
texInfo.width = _width;
texInfo.height = _height;
texInfo.usage = usage;
texInfo.format = format;
texInfo.levelCount = levelCount;
texInfo.flags = flags;
return texInfo;
}
gfx::TextureViewInfo Texture2D::getGfxTextureViewCreateInfo(gfx::Texture *texture, gfx::Format format, uint32_t baseLevel, uint32_t levelCount) {
gfx::TextureViewInfo texViewInfo;
texViewInfo.type = gfx::TextureType::TEX2D;
texViewInfo.texture = texture;
texViewInfo.format = format;
texViewInfo.baseLevel = baseLevel;
texViewInfo.levelCount = levelCount;
return texViewInfo;
}
void Texture2D::initDefault(const ccstd::optional<ccstd::string> &uuid) {
Super::initDefault(uuid);
auto *imageAsset = ccnew ImageAsset();
imageAsset->initDefault(ccstd::nullopt);
setImage(imageAsset);
}
void Texture2D::setImage(ImageAsset *value) {
ccstd::vector<IntrusivePtr<ImageAsset>> mipmaps;
if (value != nullptr) {
mipmaps.emplace_back(value);
}
setMipmaps(mipmaps);
}
bool Texture2D::validate() const {
return !_mipmaps.empty();
}
} // namespace cc

View File

@@ -0,0 +1,216 @@
/****************************************************************************
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/Ptr.h"
#include "base/std/optional.h"
#include "core/assets/Asset.h"
#include "core/assets/AssetEnum.h"
#include "core/assets/SimpleTexture.h"
namespace cc {
struct ITexture2DSerializeData {
ccstd::string base;
ccstd::vector<ccstd::string> mipmaps;
};
/**
* @en The create information for [[Texture2D]]
* @zh 用来创建贴图的信息。
*/
struct ITexture2DCreateInfo {
/**
* @en The pixel width
* @zh 像素宽度。
*/
uint32_t width{0};
/**
* @en The pixel height
* @zh 像素高度。
*/
uint32_t height{0};
/**
* @en The pixel format
* @zh 像素格式。
* @default PixelFormat.RGBA8888
*/
ccstd::optional<PixelFormat> format;
/**
* @en The mipmap level count
* @zh mipmap 层级。
* @default 1
*/
ccstd::optional<uint32_t> mipmapLevel;
/**
* @en The selected base mipmap level
* @zh 选择使用的最小 mipmap 层级。
* @default 1
*/
ccstd::optional<uint32_t> baseLevel;
/**
* @en The selected maximum mipmap level
* @zh 选择使用的最大 mipmap 层级。
* @default 1
*/
ccstd::optional<uint32_t> maxLevel;
};
/**
* @en The 2D texture asset. It supports mipmap, each level of mipmap use an [[ImageAsset]].
* @zh 二维贴图资源。二维贴图资源的每个 Mipmap 层级都为一张 [[ImageAsset]]。
*/
class Texture2D final : public SimpleTexture {
public:
using Super = SimpleTexture;
Texture2D();
~Texture2D() override;
/**
* @en All levels of mipmap images, be noted, automatically generated mipmaps are not included.
* When setup mipmap, the size of the texture and pixel format could be modified.
* @zh 所有层级 Mipmap注意这里不包含自动生成的 Mipmap。
* 当设置 Mipmap 时,贴图的尺寸以及像素格式可能会改变。
*/
const ccstd::vector<IntrusivePtr<ImageAsset>> &getMipmaps() const {
return _mipmaps;
}
const ccstd::vector<ccstd::string> &getMipmapsUuids() const { // TODO(xwx): temporary use _mipmaps as string array
return _mipmapsUuids;
}
//cjh TODO: TextureCube also needs this method.
void syncMipmapsForJS(const ccstd::vector<IntrusivePtr<ImageAsset>> &value);
void setMipmaps(const ccstd::vector<IntrusivePtr<ImageAsset>> &value);
/**
* @en Level 0 mipmap image.
* Be noted, `this.image = img` equals `this.mipmaps = [img]`,
* sets image will clear all previous mipmaps.
* @zh 0 级 Mipmap。
* 注意,`this.image = img` 等价于 `this.mipmaps = [img]`
* 也就是说,通过 `this.image` 设置 0 级 Mipmap 时将隐式地清除之前的所有 Mipmap。
*/
inline ImageAsset *getImage() const {
return _mipmaps.empty() ? nullptr : _mipmaps[0].get();
}
void setImage(ImageAsset *value);
void initialize();
void onLoaded() override;
/**
* @en Reset the current texture with given size, pixel format and mipmap images.
* After reset, the gfx resource will become invalid, you must use [[uploadData]] explicitly to upload the new mipmaps to GPU resources.
* @zh 将当前贴图重置为指定尺寸、像素格式以及指定 mipmap 层级。重置后,贴图的像素数据将变为未定义。
* mipmap 图像的数据不会自动更新到贴图中,你必须显式调用 [[uploadData]] 来上传贴图数据。
* @param info The create information
*/
void reset(const ITexture2DCreateInfo &info);
/**
* @en Reset the current texture with given size, pixel format and mipmap images.
* After reset, the gfx resource will become invalid, you must use [[uploadData]] explicitly to upload the new mipmaps to GPU resources.
* @zh 将当前贴图重置为指定尺寸、像素格式以及指定 mipmap 层级。重置后,贴图的像素数据将变为未定义。
* mipmap 图像的数据不会自动更新到贴图中,你必须显式调用 [[uploadData]] 来上传贴图数据。
* @param width Pixel width
* @param height Pixel height
* @param format Pixel format
* @param mipmapLevel Mipmap level count
* @param baseLevel Mipmap base level
* @param maxLevel Mipmap maximum level
* @deprecated since v1.0 please use [[reset]] instead
*/
void create(uint32_t width, uint32_t height, PixelFormat format = PixelFormat::RGBA8888, uint32_t mipmapLevel = 1, uint32_t baseLevel = 0, uint32_t maxLevel = 1000);
ccstd::string toString() const override;
void updateMipmaps(uint32_t firstLevel, uint32_t count) override;
/**
* @en Destroy the current 2d texture, clear up all mipmap levels and the related GPU resources.
* @zh 销毁此贴图,清空所有 Mipmap 并释放占用的 GPU 资源。
*/
bool destroy() override;
/**
* @en Gets the description of the 2d texture
* @zh 返回此贴图的描述。
* @returns The description
*/
ccstd::string description() const;
/**
* @en Release used GPU resources.
* @zh 释放占用的 GPU 资源。
* @deprecated please use [[destroy]] instead
*/
void releaseTexture();
// SERIALIZATION
/**
* @return
*/
ccstd::any serialize(const ccstd::any &ctxForExporting) override;
/**
*
* @param data
*/
void deserialize(const ccstd::any &serializedData, const ccstd::any &handle) override;
gfx::TextureInfo getGfxTextureCreateInfo(gfx::TextureUsageBit usage, gfx::Format format, uint32_t levelCount, gfx::TextureFlagBit flags) override;
gfx::TextureViewInfo getGfxTextureViewCreateInfo(gfx::Texture *texture, gfx::Format format, uint32_t baseLevel, uint32_t levelCount) override;
void initDefault(const ccstd::optional<ccstd::string> &uuid) override;
bool validate() const override;
private:
void setMipmapParams(const ccstd::vector<IntrusivePtr<ImageAsset>> &value);
ccstd::vector<IntrusivePtr<ImageAsset>> _mipmaps;
ccstd::vector<IntrusivePtr<ImageAsset>> _generatedMipmaps;
ccstd::vector<ccstd::string> _mipmapsUuids; // TODO(xwx): temporary use _mipmaps as UUIDs string array
friend class Texture2DDeserializer;
CC_DISALLOW_COPY_MOVE_ASSIGN(Texture2D);
};
} // namespace cc

View File

@@ -0,0 +1,190 @@
/****************************************************************************
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 "core/assets/TextureBase.h"
#include "base/StringUtil.h"
#include "cocos/core/platform/Debug.h"
#include "core/utils/IDGenerator.h"
#include "renderer/gfx-base/GFXDevice.h"
#include "renderer/pipeline/Define.h"
#include "base/std/hash/hash.h"
namespace cc {
namespace {
IDGenerator idGenerator("Tex");
}
TextureBase::TextureBase() {
// Id for generate hash in material
_id = idGenerator.getNewId();
_gfxDevice = getGFXDevice();
ccstd::hash_t seed = 666;
ccstd::hash_range(seed, _id.begin(), _id.end());
_textureHash = seed;
}
TextureBase::~TextureBase() = default;
void TextureBase::setWrapMode(WrapMode wrapS, WrapMode wrapT, WrapMode wrapR) {
_wrapS = wrapS;
_samplerInfo.addressU = static_cast<gfx::Address>(wrapS),
_wrapT = wrapT;
_samplerInfo.addressV = static_cast<gfx::Address>(wrapT),
_wrapR = wrapR;
_samplerInfo.addressW = static_cast<gfx::Address>(wrapR);
if (_gfxDevice != nullptr) {
_gfxSampler = _gfxDevice->getSampler(_samplerInfo);
}
notifySamplerUpdated();
}
void TextureBase::setWrapMode(WrapMode wrapS, WrapMode wrapT) {
setWrapMode(wrapS, wrapT, wrapS); // wrap modes should be as consistent as possible for performance
}
void TextureBase::setFilters(Filter minFilter, Filter magFilter) {
_minFilter = minFilter;
_samplerInfo.minFilter = static_cast<gfx::Filter>(minFilter);
_magFilter = magFilter;
_samplerInfo.magFilter = static_cast<gfx::Filter>(magFilter);
if (_gfxDevice != nullptr) {
_gfxSampler = _gfxDevice->getSampler(_samplerInfo);
}
notifySamplerUpdated();
}
void TextureBase::setMipFilter(Filter mipFilter) {
_mipFilter = mipFilter;
_samplerInfo.mipFilter = static_cast<gfx::Filter>(mipFilter);
if (_gfxDevice != nullptr) {
_gfxSampler = _gfxDevice->getSampler(_samplerInfo);
}
notifySamplerUpdated();
}
void TextureBase::setAnisotropy(uint32_t anisotropy) {
_anisotropy = anisotropy;
_samplerInfo.maxAnisotropy = anisotropy;
if (_gfxDevice != nullptr) {
_gfxSampler = _gfxDevice->getSampler(_samplerInfo);
}
notifySamplerUpdated();
}
bool TextureBase::destroy() {
const bool destroyed = Super::destroy();
//cjh TODO: if (destroyed && legacyCC.director.root?.batcher2D) {
// legacyCC.director.root.batcher2D._releaseDescriptorSetCache(this._textureHash);
// }
return destroyed;
}
gfx::Sampler *TextureBase::getGFXSampler() const {
if (_gfxSampler == nullptr) {
if (_gfxDevice != nullptr) {
const_cast<TextureBase *>(this)->_gfxSampler = _gfxDevice->getSampler(_samplerInfo);
} else {
debug::errorID(9302);
}
}
return _gfxSampler;
}
ccstd::any TextureBase::serialize(const ccstd::any & /*ctxForExporting*/) {
//cjh TODO: if (EDITOR || TEST) {
// return `${this._minFilter},${this._magFilter},${
// this._wrapS},${this._wrapT},${
// this._mipFilter},${this._anisotropy}`;
// }
return ccstd::string("");
}
void TextureBase::deserialize(const ccstd::any &serializedData, const ccstd::any & /*handle*/) {
const auto *pData = ccstd::any_cast<const ccstd::string>(&serializedData);
if (pData == nullptr) {
return;
}
const ccstd::string &data = *pData;
auto fields = StringUtil::split(data, ",");
fields.insert(fields.begin(), "");
if (fields.size() >= 5) {
// decode filters
setFilters(static_cast<Filter>(atoi(fields[1].c_str())), static_cast<Filter>(atoi(fields[2].c_str())));
// decode wraps
setWrapMode(static_cast<WrapMode>(atoi(fields[3].c_str())), static_cast<WrapMode>(atoi(fields[4].c_str())));
}
if (fields.size() >= 7) {
setMipFilter(static_cast<Filter>(atoi(fields[5].c_str())));
setAnisotropy(atoi(fields[6].c_str()));
}
}
gfx::Device *TextureBase::getGFXDevice() {
return gfx::Device::getInstance();
}
gfx::Format TextureBase::getGFXFormat() const {
return getGFXPixelFormat(_format);
}
void TextureBase::setGFXFormat(const ccstd::optional<PixelFormat> &format) {
_format = format.has_value() ? format.value() : PixelFormat::RGBA8888;
}
gfx::Format TextureBase::getGFXPixelFormat(PixelFormat format) {
if (format == PixelFormat::RGBA_ETC1) {
format = PixelFormat::RGB_ETC1;
} else if (format == PixelFormat::RGB_A_PVRTC_4BPPV1) {
format = PixelFormat::RGB_PVRTC_4BPPV1;
} else if (format == PixelFormat::RGB_A_PVRTC_2BPPV1) {
format = PixelFormat::RGB_PVRTC_2BPPV1;
}
return static_cast<gfx::Format>(format);
}
bool TextureBase::isCompressed() const {
return (_format >= PixelFormat::RGB_ETC1 && _format <= PixelFormat::RGBA_ASTC_12X12) || (_format >= PixelFormat::RGB_A_PVRTC_2BPPV1 && _format <= PixelFormat::RGBA_ETC1);
}
void TextureBase::notifySamplerUpdated() {
// emit(EventTypesToJS::TEXTURE_BASE_GFX_SAMPLER_UPDATED, _gfxSampler);
emit<SamplerUpdated>(_gfxSampler);
}
} // namespace cc

View File

@@ -0,0 +1,261 @@
/****************************************************************************
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 <cstdint>
#include "base/Ptr.h"
#include "core/assets/Asset.h"
#include "core/assets/AssetEnum.h"
#include "renderer/gfx-base/GFXDef.h"
#include "base/std/container/unordered_map.h"
#include "base/std/any.h"
namespace cc {
namespace gfx {
class Sampler;
class Device;
class Texture;
} // namespace gfx
/**
* @en The base texture class, it defines features shared by all textures.
* @zh 贴图资源基类。它定义了所有贴图共用的概念。
*/
class TextureBase : public Asset {
IMPL_EVENT_TARGET(TextureBase)
DECLARE_TARGET_EVENT_BEGIN(TextureBase)
TARGET_EVENT_ARG1(SamplerUpdated, cc::gfx::Sampler *)
DECLARE_TARGET_EVENT_END()
public:
using Super = Asset;
/**
* @en The pixel format enum.
* @zh 像素格式枚举类型
*/
using PixelFormat = cc::PixelFormat;
/**
* @en The wrap mode enum.
* @zh 环绕模式枚举类型
*/
using WrapMode = cc::WrapMode;
/**
* @en The texture filter mode enum
* @zh 纹理过滤模式枚举类型
*/
using Filter = cc::Filter;
TextureBase(); // NOTE: Editor needs to invoke 'new TextureBase' in JS, so we need to make the constructor public.
~TextureBase() override;
/**
* @en Whether the pixel data is compressed.
* @zh 此贴图是否为压缩的像素格式。
*/
bool isCompressed() const;
/**
* @en Pixel width of the texture
* @zh 此贴图的像素宽度。
*/
uint32_t getWidth() const {
return _width;
}
/**
* @en Pixel height of the texture
* @zh 此贴图的像素高度。
*/
uint32_t getHeight() const {
return _height;
}
// Functions for TS deserialization.
inline void setWidth(uint32_t width) { _width = width; }
inline void setHeight(uint32_t height) { _height = height; }
/**
* @en Gets the id of the texture
* @zh 获取标识符。
* @returns The id
*/
inline const ccstd::string &getId() const {
return _id;
}
/**
* @en Gets the pixel format
* @zh 获取像素格式。
* @returns The pixel format
*/
inline PixelFormat getPixelFormat() const {
return _format;
}
/**
* @en Gets the anisotropy
* @zh 获取各向异性。
* @returns The anisotropy
*/
inline uint32_t getAnisotropy() const {
return _anisotropy;
}
/**
* @en Sets the wrap mode of the texture.
* Be noted, if the size of the texture is not power of two, only [[WrapMode.CLAMP_TO_EDGE]] is allowed.
* @zh 设置此贴图的缠绕模式。
* 注意,若贴图尺寸不是 2 的整数幂,缠绕模式仅允许 [[WrapMode.CLAMP_TO_EDGE]]。
* @param wrapS S(U) coordinate wrap mode
* @param wrapT T(V) coordinate wrap mode
* @param wrapR R(W) coordinate wrap mode
*/
void setWrapMode(WrapMode wrapS, WrapMode wrapT, WrapMode wrapR);
void setWrapMode(WrapMode wrapS, WrapMode wrapT);
/**
* @en Sets the texture's filter mode
* @zh 设置此贴图的过滤算法。
* @param minFilter Filter mode for scale down
* @param magFilter Filter mode for scale up
*/
void setFilters(Filter minFilter, Filter magFilter);
/**
* @en Sets the texture's mip filter
* @zh 设置此贴图的缩小过滤算法。
* @param mipFilter Filter mode for scale down
*/
void setMipFilter(Filter mipFilter);
/**
* @en Sets the texture's anisotropy
* @zh 设置此贴图的各向异性。
* @param anisotropy
*/
void setAnisotropy(uint32_t anisotropy);
/**
* @en Destroy the current texture, clear up the related GPU resources.
* @zh 销毁此贴图,并释放占用的 GPU 资源。
*/
bool destroy() override;
/**
* @en Gets the texture hash.
* @zh 获取此贴图的哈希值。
*/
inline ccstd::hash_t getHash() const {
return _textureHash;
}
/**
* @en Gets the GFX Texture resource
* @zh 获取此贴图底层的 GFX 贴图对象。
*/
virtual gfx::Texture *getGFXTexture() const {
return nullptr;
}
/**
* @en Gets the internal GFX sampler information.
* @zh 获取此贴图内部使用的 GFX 采样器信息。
* @private
*/
virtual const gfx::SamplerInfo &getSamplerInfo() const {
return _samplerInfo;
}
/**
* @en Gets the sampler resource for the texture
* @zh 获取此贴图底层的 GFX 采样信息。
*/
virtual gfx::Sampler *getGFXSampler() const;
// SERIALIZATION
/**
* @return
*/
ccstd::any serialize(const ccstd::any &ctxForExporting) override;
/**
*
* @param data
*/
void deserialize(const ccstd::any &serializedData, const ccstd::any &handle) override;
protected:
static gfx::Device *getGFXDevice();
static gfx::Format getGFXPixelFormat(PixelFormat format);
gfx::Format getGFXFormat() const;
void setGFXFormat(const ccstd::optional<PixelFormat> &format);
private:
void notifySamplerUpdated();
public:
/*@serializable*/
PixelFormat _format{PixelFormat::RGBA8888};
/*@serializable*/
Filter _minFilter{Filter::LINEAR};
/*@serializable*/
Filter _magFilter{Filter::LINEAR};
/*@serializable*/
Filter _mipFilter{Filter::NONE};
/*@serializable*/
WrapMode _wrapS{WrapMode::REPEAT};
/*@serializable*/
WrapMode _wrapT{WrapMode::REPEAT};
/*@serializable*/
WrapMode _wrapR{WrapMode::REPEAT};
/*@serializable*/
uint32_t _anisotropy{0};
protected:
uint32_t _width{1};
uint32_t _height{1};
ccstd::string _id;
gfx::SamplerInfo _samplerInfo;
gfx::Sampler *_gfxSampler{nullptr};
gfx::Device *_gfxDevice{nullptr};
ccstd::hash_t _textureHash{0U};
private:
CC_DISALLOW_COPY_MOVE_ASSIGN(TextureBase);
};
} // namespace cc

View File

@@ -0,0 +1,407 @@
/****************************************************************************
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 "core/assets/TextureCube.h"
#include "core/assets/ImageAsset.h"
#include "core/assets/Texture2D.h"
#include "renderer/gfx-base/GFXTexture.h"
namespace cc {
namespace {
using ForEachFaceCallback = std::function<void(ImageAsset *face, TextureCube::FaceIndex faceIndex)>;
/**
* @param {Mipmap} mipmap
* @param {(face: ImageAsset) => void} callback
*/
void forEachFace(const ITextureCubeMipmap &mipmap, const ForEachFaceCallback &callback) {
callback(mipmap.front, TextureCube::FaceIndex::FRONT);
callback(mipmap.back, TextureCube::FaceIndex::BACK);
callback(mipmap.left, TextureCube::FaceIndex::LEFT);
callback(mipmap.right, TextureCube::FaceIndex::RIGHT);
callback(mipmap.top, TextureCube::FaceIndex::TOP);
callback(mipmap.bottom, TextureCube::FaceIndex::BOTTOM);
}
} // namespace
/* static */
TextureCube *TextureCube::fromTexture2DArray(const ccstd::vector<Texture2D *> &textures) {
size_t nMipmaps = textures.size() / 6;
ccstd::vector<ITextureCubeMipmap> mipmaps;
mipmaps.reserve(nMipmaps);
for (size_t i = 0; i < nMipmaps; i++) {
size_t x = i * 6;
ITextureCubeMipmap mipmap;
mipmap.front = textures[x + static_cast<uint32_t>(FaceIndex::FRONT)]->getImage(),
mipmap.back = textures[x + static_cast<uint32_t>(FaceIndex::BACK)]->getImage(),
mipmap.left = textures[x + static_cast<uint32_t>(FaceIndex::LEFT)]->getImage(),
mipmap.right = textures[x + static_cast<uint32_t>(FaceIndex::RIGHT)]->getImage(),
mipmap.top = textures[x + static_cast<uint32_t>(FaceIndex::TOP)]->getImage(),
mipmap.bottom = textures[x + static_cast<uint32_t>(FaceIndex::BOTTOM)]->getImage(),
mipmaps.emplace_back(mipmap);
}
auto *out = ccnew TextureCube();
out->setMipmaps(mipmaps);
return out;
}
TextureCube::TextureCube() = default;
TextureCube::~TextureCube() = default;
void TextureCube::setMipmaps(const ccstd::vector<ITextureCubeMipmap> &value) {
_mipmaps = value;
auto cubeMaps = ccstd::vector<ITextureCubeMipmap>{};
if (value.size() == 1) {
const auto &cubeMipmap = value.at(0);
const auto &front = cubeMipmap.front->extractMipmaps();
const auto &back = cubeMipmap.back->extractMipmaps();
const auto &left = cubeMipmap.left->extractMipmaps();
const auto &right = cubeMipmap.right->extractMipmaps();
const auto &top = cubeMipmap.top->extractMipmaps();
const auto &bottom = cubeMipmap.bottom->extractMipmaps();
if (front.size() != back.size() ||
front.size() != left.size() ||
front.size() != right.size() ||
front.size() != top.size() ||
front.size() != bottom.size()) {
assert("different faces should have the same mipmap level");
this->setMipmapParams({});
return;
}
const auto level = front.size();
for (auto i = 0U; i < level; i++) {
const auto cubeMap = ITextureCubeMipmap{
front[i],
back[i],
left[i],
right[i],
top[i],
bottom[i],
};
cubeMaps.emplace_back(cubeMap);
}
} else if (value.size() > 1) {
for (const auto &mipmap : value) {
const auto cubeMap = ITextureCubeMipmap{
mipmap.front->extractMipmap0(),
mipmap.back->extractMipmap0(),
mipmap.left->extractMipmap0(),
mipmap.right->extractMipmap0(),
mipmap.top->extractMipmap0(),
mipmap.bottom->extractMipmap0(),
};
cubeMaps.emplace_back(cubeMap);
}
}
setMipmapParams(cubeMaps);
}
void TextureCube::setMipmapParams(const ccstd::vector<ITextureCubeMipmap> &value) {
_generatedMipmaps = value;
setMipmapLevel(static_cast<uint32_t>(_generatedMipmaps.size()));
if (!_generatedMipmaps.empty()) {
ImageAsset *imageAsset = _generatedMipmaps[0].front;
reset({imageAsset->getWidth(),
imageAsset->getHeight(),
imageAsset->getFormat(),
static_cast<uint32_t>(_generatedMipmaps.size()),
_baseLevel,
_maxLevel});
for (size_t level = 0, len = _generatedMipmaps.size(); level < len; ++level) {
forEachFace(_generatedMipmaps[level], [this, level](ImageAsset *face, TextureCube::FaceIndex faceIndex) {
assignImage(face, static_cast<uint32_t>(level), static_cast<uint32_t>(faceIndex));
});
}
} else {
reset({0,
0,
ccstd::nullopt,
static_cast<uint32_t>(_generatedMipmaps.size()),
_baseLevel,
_maxLevel});
}
}
void TextureCube::setMipmapAtlas(const TextureCubeMipmapAtlasInfo &value) {
if (value.layout.empty()) {
return;
}
_mipmapAtlas = value;
const ITextureCubeMipmap &atlas = _mipmapAtlas.atlas;
const ccstd::vector<MipmapAtlasLayoutInfo> &layouts = _mipmapAtlas.layout;
setMipmapLevel(static_cast<uint32_t>(layouts.size()));
const MipmapAtlasLayoutInfo &lv0Layout = layouts[0];
const ImageAsset *imageAsset = atlas.front;
reset({lv0Layout.width,
lv0Layout.height,
imageAsset->getFormat(),
static_cast<uint32_t>(layouts.size()),
_baseLevel,
_maxLevel});
const uint32_t pixelSize = gfx::GFX_FORMAT_INFOS[static_cast<uint32_t>(imageAsset->getFormat())].size;
for (size_t level = 0; level < layouts.size(); level++) {
const MipmapAtlasLayoutInfo &layoutInfo = layouts[level];
uint32_t currentSize = layoutInfo.width * layoutInfo.height * pixelSize;
//Upload 6 sides by level
forEachFace(atlas, [this, currentSize, lv0Layout, layoutInfo, level, pixelSize](ImageAsset *face, TextureCube::FaceIndex faceIndex) {
auto *buffer = ccnew uint8_t[currentSize];
memset(buffer, 0, currentSize);
const uint8_t *data = face->getData();
//Splitting Atlas
if (level == 0) {
memcpy(buffer, data, currentSize);
} else {
uint32_t bufferOffset = 0;
uint32_t dateOffset = lv0Layout.width * lv0Layout.height * pixelSize;
uint32_t leftOffset = layoutInfo.left * pixelSize;
for (size_t j = 0; j < layoutInfo.height; j++) {
memcpy(buffer + bufferOffset, data + dateOffset + leftOffset, layoutInfo.width * pixelSize);
bufferOffset += layoutInfo.width * pixelSize;
dateOffset += lv0Layout.width * pixelSize;
}
}
auto *tempAsset = ccnew ImageAsset();
tempAsset->addRef();
auto *arrayBuffer = ccnew ArrayBuffer(buffer, static_cast<uint32_t>(currentSize));
IMemoryImageSource source{arrayBuffer, face->isCompressed(), layoutInfo.width, layoutInfo.height, face->getFormat()};
tempAsset->setNativeAsset(source);
assignImage(tempAsset, static_cast<uint32_t>(level), static_cast<uint32_t>(faceIndex));
CC_SAFE_DELETE_ARRAY(buffer);
tempAsset->release();
tempAsset = nullptr;
});
}
}
void TextureCube::setMipmapsForJS(const ccstd::vector<ITextureCubeMipmap> &value) {
_mipmaps = value;
}
void TextureCube::setMipmapAtlasForJS(const TextureCubeMipmapAtlasInfo &value) {
_mipmapAtlas = value;
}
void TextureCube::setImage(const ITextureCubeMipmap *value) {
if (value != nullptr) {
setMipmaps({*value});
} else {
setMipmaps({});
}
}
void TextureCube::reset(const ITextureCubeCreateInfo &info) {
_width = info.width;
_height = info.height;
setGFXFormat(info.format);
uint32_t mipLevels = info.mipmapLevel.has_value() ? info.mipmapLevel.value() : 1;
setMipmapLevel(mipLevels);
uint32_t minLod = info.baseLevel.has_value() ? info.baseLevel.value() : 0;
uint32_t maxLod = info.maxLevel.has_value() ? info.maxLevel.value() : 1000;
setMipRange(minLod, maxLod);
tryReset();
}
void TextureCube::releaseTexture() {
destroy();
}
void TextureCube::updateMipmaps(uint32_t firstLevel, uint32_t count) {
if (firstLevel >= _generatedMipmaps.size()) {
return;
}
auto nUpdate = static_cast<uint32_t>(std::min(
count == 0 ? _generatedMipmaps.size() : count,
_generatedMipmaps.size() - firstLevel));
for (uint32_t i = 0; i < nUpdate; ++i) {
uint32_t level = firstLevel + i;
forEachFace(_generatedMipmaps[level], [this, level](auto face, auto faceIndex) {
assignImage(face, level, static_cast<uint32_t>(faceIndex));
});
}
}
bool TextureCube::isUsingOfflineMipmaps() {
return _mipmapMode == MipmapMode::BAKED_CONVOLUTION_MAP;
}
void TextureCube::initialize() {
if (_mipmapMode == MipmapMode::BAKED_CONVOLUTION_MAP) {
setMipmapAtlas(_mipmapAtlas);
} else {
setMipmaps(_mipmaps);
}
}
void TextureCube::onLoaded() {
initialize();
}
bool TextureCube::destroy() {
_mipmaps.clear();
_generatedMipmaps.clear();
_mipmapAtlas.layout.clear();
return Super::destroy();
}
ccstd::any TextureCube::serialize(const ccstd::any & /*ctxForExporting*/) {
//cjh TODO: if (EDITOR || TEST) {
// return {
// base: super._serialize(ctxForExporting),
// rgbe: this.isRGBE,
// mipmaps: this._mipmaps.map((mipmap) => ((ctxForExporting && ctxForExporting._compressUuid) ? {
// front: EditorExtends.UuidUtils.compressUuid(mipmap.front._uuid, true),
// back: EditorExtends.UuidUtils.compressUuid(mipmap.back._uuid, true),
// left: EditorExtends.UuidUtils.compressUuid(mipmap.left._uuid, true),
// right: EditorExtends.UuidUtils.compressUuid(mipmap.right._uuid, true),
// top: EditorExtends.UuidUtils.compressUuid(mipmap.top._uuid, true),
// bottom: EditorExtends.UuidUtils.compressUuid(mipmap.bottom._uuid, true),
// } : {
// front: mipmap.front._uuid,
// back: mipmap.back._uuid,
// left: mipmap.left._uuid,
// right: mipmap.right._uuid,
// top: mipmap.top._uuid,
// bottom: mipmap.bottom._uuid,
// })),
// };
// }
return nullptr;
}
void TextureCube::deserialize(const ccstd::any &serializedData, const ccstd::any &handle) {
const auto *data = ccstd::any_cast<TextureCubeSerializeData>(&serializedData);
if (data == nullptr) {
return;
}
Super::deserialize(data->base, handle);
isRGBE = data->rgbe;
_mipmapMode = data->mipmapMode;
_mipmaps.resize(data->mipmaps.size());
for (size_t i = 0; i < data->mipmaps.size(); ++i) {
// Prevent resource load failed
ITextureCubeMipmap mipmap;
mipmap.front = ccnew ImageAsset(),
mipmap.back = ccnew ImageAsset(),
mipmap.left = ccnew ImageAsset(),
mipmap.right = ccnew ImageAsset(),
mipmap.top = ccnew ImageAsset(),
mipmap.bottom = ccnew ImageAsset();
_mipmaps[i] = mipmap;
// auto* mipmap = data->mipmaps[i];
//cjh TODO: what's handle.result?? const imageAssetClassId = js.getClassId(ImageAsset);
//
// handle.result.push(this._mipmaps[i], `front`, mipmap.front, imageAssetClassId);
// handle.result.push(this._mipmaps[i], `back`, mipmap.back, imageAssetClassId);
// handle.result.push(this._mipmaps[i], `left`, mipmap.left, imageAssetClassId);
// handle.result.push(this._mipmaps[i], `right`, mipmap.right, imageAssetClassId);
// handle.result.push(this._mipmaps[i], `top`, mipmap.top, imageAssetClassId);
// handle.result.push(this._mipmaps[i], `bottom`, mipmap.bottom, imageAssetClassId);
}
}
gfx::TextureInfo TextureCube::getGfxTextureCreateInfo(gfx::TextureUsageBit usage, gfx::Format format, uint32_t levelCount, gfx::TextureFlagBit flags) {
gfx::TextureInfo texInfo;
texInfo.type = gfx::TextureType::CUBE;
texInfo.width = _width;
texInfo.height = _height;
texInfo.layerCount = 6;
texInfo.usage = usage;
texInfo.format = format;
texInfo.levelCount = levelCount;
texInfo.flags = flags;
return texInfo;
}
gfx::TextureViewInfo TextureCube::getGfxTextureViewCreateInfo(gfx::Texture *texture, gfx::Format format, uint32_t baseLevel, uint32_t levelCount) {
gfx::TextureViewInfo texViewInfo;
texViewInfo.type = gfx::TextureType::CUBE;
texViewInfo.baseLayer = 0;
texViewInfo.layerCount = 6;
texViewInfo.texture = texture;
texViewInfo.format = format;
texViewInfo.baseLevel = baseLevel;
texViewInfo.levelCount = levelCount;
return texViewInfo;
}
void TextureCube::initDefault(const ccstd::optional<ccstd::string> &uuid) {
Super::initDefault(uuid);
auto *imageAsset = ccnew ImageAsset();
imageAsset->initDefault(ccstd::nullopt);
ITextureCubeMipmap mipmap;
mipmap.front = imageAsset;
mipmap.back = imageAsset;
mipmap.top = imageAsset;
mipmap.bottom = imageAsset;
mipmap.left = imageAsset;
mipmap.right = imageAsset;
setMipmaps({mipmap});
}
bool TextureCube::validate() const {
if (_mipmapMode == MipmapMode::BAKED_CONVOLUTION_MAP) {
if (_mipmapAtlas.layout.empty()) {
return false;
}
return (_mipmapAtlas.atlas.top && _mipmapAtlas.atlas.bottom && _mipmapAtlas.atlas.front && _mipmapAtlas.atlas.back && _mipmapAtlas.atlas.left && _mipmapAtlas.atlas.right);
}
if (_mipmaps.empty()) {
return false;
}
return std::all_of(_mipmaps.begin(),
_mipmaps.end(),
[&](const ITextureCubeMipmap &mipmap) {
return (mipmap.top && mipmap.bottom && mipmap.front && mipmap.back && mipmap.left && mipmap.right);
});
}
} // namespace cc

View File

@@ -0,0 +1,270 @@
/****************************************************************************
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 "core/assets/Asset.h"
#include "core/assets/SimpleTexture.h"
namespace cc {
class ImageAsset;
class Texture2D;
struct ITexture2DCreateInfo;
using ITextureCubeCreateInfo = ITexture2DCreateInfo;
/**
* @en The texture cube mipmap interface
* @zh 立方体贴图的 Mipmap 接口。
*/
struct ITextureCubeMipmap {
IntrusivePtr<ImageAsset> front;
IntrusivePtr<ImageAsset> back;
IntrusivePtr<ImageAsset> left;
IntrusivePtr<ImageAsset> right;
IntrusivePtr<ImageAsset> top;
IntrusivePtr<ImageAsset> bottom;
};
struct ITextureCubeSerializeMipmapData {
ccstd::string front;
ccstd::string back;
ccstd::string left;
ccstd::string right;
ccstd::string top;
ccstd::string bottom;
};
/**
* @en The MipmapAtlas region interface
* @zh MipmapAtlas的region接口。
*/
struct MipmapAtlasLayoutInfo {
uint32_t left{0};
uint32_t top{0};
uint32_t width{0};
uint32_t height{0};
uint32_t level{0};
};
/**
* @en The texture cube MipmapAtlas interface
* @zh 立方体贴图的 MipmapAtlas 接口。
*/
struct TextureCubeMipmapAtlasInfo {
ITextureCubeMipmap atlas;
ccstd::vector<MipmapAtlasLayoutInfo> layout;
};
/**
* @en The way to fill mipmaps.
* @zh 填充mipmaps的方式。
*/
enum class MipmapMode {
/**
* @zh
* 不使用mipmaps
* @en
* Not using mipmaps
* @readonly
*/
NONE = 0,
/**
* @zh
* 使用自动生成的mipmaps
* @en
* Using the automatically generated mipmaps
* @readonly
*/
AUTO = 1,
/**
* @zh
* 使用卷积图填充mipmaps
* @en
* Filling mipmaps with convolutional maps.
* @readonly
*/
BAKED_CONVOLUTION_MAP = 2
};
struct TextureCubeSerializeData {
ccstd::string base;
bool rgbe{false};
MipmapMode mipmapMode{MipmapMode::NONE};
ccstd::vector<ITextureCubeSerializeMipmapData> mipmaps;
TextureCubeMipmapAtlasInfo mipmapAtlas;
};
/**
* @en The texture cube asset.
* Each mipmap level of a texture cube have 6 [[ImageAsset]], represents 6 faces of the cube.
* @zh 立方体贴图资源。
* 立方体贴图资源的每个 Mipmap 层级都为 6 张 [[ImageAsset]],分别代表了立方体贴图的 6 个面。
*/
class TextureCube final : public SimpleTexture {
public:
using Super = SimpleTexture;
TextureCube();
~TextureCube() override;
/**
* @en The index for all faces of the cube
* @zh 立方体每个面的约定索引。
*/
enum class FaceIndex {
RIGHT = 0,
LEFT = 1,
TOP = 2,
BOTTOM = 3,
FRONT = 4,
BACK = 5,
};
/**
* @en Create a texture cube with an array of [[Texture2D]] which represents 6 faces of the texture cube.
* @zh 通过二维贴图数组指定每个 Mipmap 的每个面创建立方体贴图。
* @param textures Texture array, the texture count must be multiple of 6. Every 6 textures are 6 faces of a mipmap level.
* The order should obey [[FaceIndex]] order.
* @param out Output texture cube, if not given, will create a new texture cube.
* @returns The created texture cube.
* @example
* ```ts
* const textures = new Array<Texture2D>(6);
* textures[TextureCube.FaceIndex.front] = frontImage;
* textures[TextureCube.FaceIndex.back] = backImage;
* textures[TextureCube.FaceIndex.left] = leftImage;
* textures[TextureCube.FaceIndex.right] = rightImage;
* textures[TextureCube.FaceIndex.top] = topImage;
* textures[TextureCube.FaceIndex.bottom] = bottomImage;
* const textureCube = TextureCube.fromTexture2DArray(textures);
* ```
*/
static TextureCube *fromTexture2DArray(const ccstd::vector<Texture2D *> &textures);
/**
* @en All levels of mipmap images, be noted, automatically generated mipmaps are not included.
* When setup mipmap, the size of the texture and pixel format could be modified.
* @zh 所有层级 Mipmap注意这里不包含自动生成的 Mipmap。
* 当设置 Mipmap 时,贴图的尺寸以及像素格式可能会改变。
*/
const ccstd::vector<ITextureCubeMipmap> &getMipmaps() const {
return _mipmaps;
}
inline const TextureCubeMipmapAtlasInfo &getMipmapAtlas() const {
return _mipmapAtlas;
}
void setMipmaps(const ccstd::vector<ITextureCubeMipmap> &value);
void setMipmapsForJS(const ccstd::vector<ITextureCubeMipmap> &value);
void setMipmapAtlasForJS(const TextureCubeMipmapAtlasInfo &value);
/**
* @en Fill mipmaps with convolutional maps.
* @zh 使用卷积图填充mipmaps。
* @param value All mipmaps of each face of the cube map are stored in the form of atlas.
* and the value contains the atlas of the 6 faces and the layout information of each mipmap layer.
*/
void setMipmapAtlas(const TextureCubeMipmapAtlasInfo &value);
/**
* @en Level 0 mipmap image.
* Be noted, `this.image = img` equals `this.mipmaps = [img]`,
* sets image will clear all previous mipmaps.
* @zh 0 级 Mipmap。
* 注意,`this.image = img` 等价于 `this.mipmaps = [img]`
* 也就是说,通过 `this.image` 设置 0 级 Mipmap 时将隐式地清除之前的所有 Mipmap。
*/
const ITextureCubeMipmap *getImage() const {
return _mipmaps.empty() ? nullptr : &_mipmaps[0];
}
void setImage(const ITextureCubeMipmap *value);
/**
* @en Reset the current texture with given size, pixel format and mipmap images.
* After reset, the gfx resource will become invalid, you must use [[uploadData]] explicitly to upload the new mipmaps to GPU resources.
* @zh 将当前贴图重置为指定尺寸、像素格式以及指定 mipmap 层级。重置后,贴图的像素数据将变为未定义。
* mipmap 图像的数据不会自动更新到贴图中,你必须显式调用 [[uploadData]] 来上传贴图数据。
* @param info The create information
*/
void reset(const ITextureCubeCreateInfo &info);
/**
* @en Release used GPU resources.
* @zh 释放占用的 GPU 资源。
* @deprecated please use [[destroy]] instead
*/
void releaseTexture();
// Override functions
void updateMipmaps(uint32_t firstLevel, uint32_t count) override;
/**
* @en Whether mipmaps are baked convolutional maps.
* @zh mipmaps是否为烘焙出来的卷积图。
*/
bool isUsingOfflineMipmaps() override;
void initialize();
void onLoaded() override;
/**
* 销毁此贴图,清空所有 Mipmap 并释放占用的 GPU 资源。
*/
bool destroy() override;
ccstd::any serialize(const ccstd::any &ctxForExporting) override;
void deserialize(const ccstd::any &serializedData, const ccstd::any &handle) override;
gfx::TextureInfo getGfxTextureCreateInfo(gfx::TextureUsageBit usage, gfx::Format format, uint32_t levelCount, gfx::TextureFlagBit flags) override;
gfx::TextureViewInfo getGfxTextureViewCreateInfo(gfx::Texture *texture, gfx::Format format, uint32_t baseLevel, uint32_t levelCount) override;
void initDefault(const ccstd::optional<ccstd::string> &uuid) override;
bool validate() const override;
//
/*@serializable*/
MipmapMode _mipmapMode{MipmapMode::NONE};
/*@serializable*/
bool isRGBE{false};
private:
void setMipmapParams(const ccstd::vector<ITextureCubeMipmap> &value);
/*@serializable*/
ccstd::vector<ITextureCubeMipmap> _mipmaps;
ccstd::vector<ITextureCubeMipmap> _generatedMipmaps;
/*@serializable*/
TextureCubeMipmapAtlasInfo _mipmapAtlas;
CC_DISALLOW_COPY_MOVE_ASSIGN(TextureCube);
};
} // namespace cc