* Copyright (C) 2025 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "SceneImpl.h"
#include "Vec2Impl.h"
#include "interop_js/arkts_interop_js_api.h"
#include "interop_js/arkts_esvalue.h"
#include "SceneJS.h"
#include "CheckNapiEnv.h"
#include "RenderConfigurationImpl.h"
#include "RenderContextImpl.h"
#include "scene_adapter/scene_adapter.h"
namespace OHOS::Render3D::KITETS {
SceneImpl::SceneImpl(const std::string& uriStr)
{
sceneETS_ = std::make_shared<SceneETS>();
SceneETS::SceneLoadParams sceneLoadParams;
if (!sceneETS_->Load(uriStr, sceneLoadParams)) {
WIDGET_LOGE("ohos_lume loadScene fail");
}
}
SceneImpl::SceneImpl(const std::string &uriStr, ::SceneTH::SceneLoadParams params)
{
sceneETS_ = std::make_shared<SceneETS>();
SceneETS::SceneLoadParams sceneLoadParams = ExtractSceneLoadParams(params);
if (!sceneETS_->Load(uriStr, sceneLoadParams)) {
WIDGET_LOGE("ohos_lume loadScene fail");
}
}
SceneImpl::SceneImpl(SCENE_NS::IScene::Ptr scene, std::shared_ptr<OHOS::Render3D::ISceneAdapter> sceneAdapter)
{
sceneETS_ = std::make_shared<SceneETS>(scene, sceneAdapter);
if (!sceneETS_) {
WIDGET_LOGE("ohos_lume loadScene fail");
}
}
SceneImpl::~SceneImpl()
{
destroy();
}
bool SceneImpl::renderFrame(::taihe::optional_view<::SceneTH::RenderParameters> params)
{
if (!sceneETS_) {
return false;
}
SceneETS::RenderParameters renderParams = ExtractRenderParameters(params);
return sceneETS_->RenderFrame(renderParams);
}
::SceneResources::Environment SceneImpl::getEnvironment()
{
if (!sceneETS_) {
return ::SceneResources::Environment({nullptr, nullptr});
}
InvokeReturn<std::shared_ptr<EnvironmentETS>> environemnt = sceneETS_->GetEnvironment();
if (environemnt) {
return taihe::make_holder<EnvironmentImpl, ::SceneResources::Environment>(environemnt.value);
} else {
WIDGET_LOGE("getEnvironment error: %s", environemnt.error.c_str());
return ::SceneResources::Environment({nullptr, nullptr});
}
}
void SceneImpl::setEnvironment(::SceneResources::weak::Environment env)
{
if (!sceneETS_ || env.is_error()) {
return;
}
auto envOptional = static_cast<::SceneResources::weak::SceneResource>(env)->getImpl();
if (!envOptional.has_value()) {
WIDGET_LOGE("invalid environment in taihe object");
return;
}
auto envImpl = reinterpret_cast<EnvironmentImpl*>(envOptional.value());
if (envImpl) {
sceneETS_->SetEnvironment(envImpl->GetEnvETS());
} else {
WIDGET_LOGE("Invalid environment in setEnvironment");
}
}
::taihe::array<::SceneResources::Animation> SceneImpl::getAnimations()
{
if (!sceneETS_) {
return {};
}
if (animations_) {
return animations_.value();
}
std::vector<std::shared_ptr<AnimationETS>> animationETSlist =
sceneETS_->GetAnimations();
std::vector<::SceneResources::Animation> result;
for (const auto& animationETS : animationETSlist) {
result.emplace_back(AnimationImpl::createAnimationFromETS(animationETS));
}
animations_ = taihe::array<::SceneResources::Animation>(result);
return animations_.value();
}
::SceneTH::RenderContextOrNull getDefaultRenderContext()
{
if (!OHOS::Render3D::SceneAdapter::IsEngineInitSuccessful()) {
return ::SceneTH::RenderContextOrNull::make_nValue();
}
auto rc = taihe::make_holder<RenderContextImpl, ::SceneTH::RenderContext>();
return ::SceneTH::RenderContextOrNull::make_rc(rc);
}
::SceneTH::Scene loadScene(::taihe::optional_view<uintptr_t> uri)
{
std::string uriStr = ExtractUri(uri);
if (uriStr.empty()) {
uriStr = "scene://empty";
}
::SceneTH::Scene scene = taihe::make_holder<SceneImpl, ::SceneTH::Scene>(uriStr);
return scene;
}
::SceneTH::Scene loadSceneWithLoadParams(uintptr_t uri, ::SceneTH::SceneLoadParams const& params)
{
double val = 0.0;
if (params.offset) {
val = params.offset.value();
}
std::string uriStr;
if (val < 0.0) {
WIDGET_LOGE("offset must not be negative");
uriStr.insert(0, "file://");
return taihe::make_holder<SceneImpl, ::SceneTH::Scene>(uriStr, params);
}
if (val != static_cast<double>(static_cast<int64_t>(val))) {
WIDGET_LOGE("offset must be an integer, got a float");
uriStr.insert(0, "file://");
return taihe::make_holder<SceneImpl, ::SceneTH::Scene>(uriStr, params);
}
if (val > static_cast<double>(std::numeric_limits<int64_t>::max())) {
WIDGET_LOGE("offset exceeds int64 range");
uriStr.insert(0, "file://");
return taihe::make_holder<SceneImpl, ::SceneTH::Scene>(uriStr, params);
}
uriStr = ExtractUri(uri);
if (uriStr.empty()) {
uriStr = "scene://empty";
}
::SceneTH::Scene scene = taihe::make_holder<SceneImpl, ::SceneTH::Scene>(uriStr, params);
return scene;
}
::SceneNodes::VariousNodes SceneImpl::importNode(::taihe::string_view name, ::SceneNodes::weak::Node node,
::SceneNodes::NodeOrNull parent)
{
if (!sceneETS_ || node.is_error()) {
WIDGET_LOGE("can't import node, the scene or node is invalid!");
return SceneNodes::VariousNodes::make_node(taihe::make_holder<NodeImpl, ::SceneNodes::Node>(nullptr));
}
auto nodeOptional = static_cast<::SceneResources::weak::SceneResource>(node)->getImpl();
if (!nodeOptional.has_value()) {
WIDGET_LOGE("invalid node in taihe object");
return SceneNodes::VariousNodes::make_node(taihe::make_holder<NodeImpl, ::SceneNodes::Node>(nullptr));
}
auto nodeImpl = reinterpret_cast<NodeImpl*>(nodeOptional.value());
if (!nodeImpl) {
WIDGET_LOGE("Invalid node in importNode");
return SceneNodes::VariousNodes::make_node(taihe::make_holder<NodeImpl, ::SceneNodes::Node>(nullptr));
}
std::shared_ptr<NodeETS> res{nullptr};
if (parent.holds_node()) {
auto parentNodeOptional = static_cast<::SceneResources::weak::SceneResource>(parent.get_node_ref())->getImpl();
if (!parentNodeOptional.has_value()) {
WIDGET_LOGE("invalid node in taihe object");
return SceneNodes::VariousNodes::make_node(taihe::make_holder<NodeImpl, ::SceneNodes::Node>(nullptr));
}
auto parentNodeImpl = reinterpret_cast<NodeImpl*>(parentNodeOptional.value());
if (!parentNodeImpl) {
WIDGET_LOGE("Invalid parent node in importNode");
return SceneNodes::VariousNodes::make_node(taihe::make_holder<NodeImpl, ::SceneNodes::Node>(nullptr));
}
res = sceneETS_->ImportNode(std::string(name), nodeImpl->GetInternalNode(), parentNodeImpl->GetInternalNode());
} else {
res = sceneETS_->ImportNode(std::string(name), nodeImpl->GetInternalNode(), nullptr);
}
if (!res) {
WIDGET_LOGE("ImportNode fail with null value");
return SceneNodes::VariousNodes::make_node(taihe::make_holder<NodeImpl, ::SceneNodes::Node>(nullptr));
}
animations_ = std::nullopt;
return NodeImpl::MakeVariousNodes(res);
}
::SceneNodes::VariousNodes SceneImpl::importScene(
::taihe::string_view name, ::SceneTH::weak::Scene scene, ::SceneNodes::NodeOrNull parent)
{
if (!sceneETS_ || scene.is_error()) {
WIDGET_LOGE("can't import scene, the scene or the scene to be imported is invalid!");
return SceneNodes::VariousNodes::make_node(taihe::make_holder<NodeImpl, ::SceneNodes::Node>(nullptr));
}
::taihe::optional<int64_t> sceneOptional = scene->getImpl();
if (!sceneOptional.has_value()) {
WIDGET_LOGE("Invalid scene in importScene");
return SceneNodes::VariousNodes::make_node(taihe::make_holder<NodeImpl, ::SceneNodes::Node>(nullptr));
}
auto sceneImpl = reinterpret_cast<SceneImpl*>(sceneOptional.value());
std::shared_ptr<NodeETS> res{nullptr};
if (parent.holds_node()) {
auto parentNodeOptional = static_cast<::SceneResources::weak::SceneResource>(parent.get_node_ref())->getImpl();
if (!parentNodeOptional.has_value()) {
WIDGET_LOGE("invalid node in taihe object");
return SceneNodes::VariousNodes::make_node(taihe::make_holder<NodeImpl, ::SceneNodes::Node>(nullptr));
}
auto parentNodeImpl = reinterpret_cast<NodeImpl*>(parentNodeOptional.value());
if (!parentNodeImpl) {
WIDGET_LOGE("Invalid parent node in importNode");
return SceneNodes::VariousNodes::make_node(taihe::make_holder<NodeImpl, ::SceneNodes::Node>(nullptr));
}
res =
sceneETS_->ImportScene(std::string(name), sceneImpl->getInternalScene(), parentNodeImpl->GetInternalNode());
} else {
res = sceneETS_->ImportScene(std::string(name), sceneImpl->getInternalScene(), nullptr);
}
if (!res) {
WIDGET_LOGE("ImportNode fail with null value");
return SceneNodes::VariousNodes::make_node(taihe::make_holder<NodeImpl, ::SceneNodes::Node>(nullptr));
}
animations_ = std::nullopt;
return NodeImpl::MakeVariousNodes(res);
}
::SceneTH::Scene sceneTransferStaticImpl(uintptr_t input)
{
WIDGET_LOGI("sceneTransferStaticImpl");
ani_object esValue = reinterpret_cast<ani_object>(input);
void* nativePtr = nullptr;
if (!arkts_esvalue_unwrap(taihe::get_env(), esValue, &nativePtr) || nativePtr == nullptr) {
WIDGET_LOGE("unwrap esvalue failed");
return ::SceneTH::Scene({nullptr, nullptr});
}
auto sceneJS = reinterpret_cast<SceneJS*>(nativePtr);
if (!sceneJS) {
WIDGET_LOGE("transfer scene failed");
return ::SceneTH::Scene({nullptr, nullptr});
}
SCENE_NS::IScene::Ptr scene = sceneJS->GetNativeObject<SCENE_NS::IScene>();
std::shared_ptr<OHOS::Render3D::ISceneAdapter> sceneAdapter = sceneJS->scene_;
return taihe::make_holder<SceneImpl, ::SceneTH::Scene>(scene, sceneAdapter);
}
uintptr_t sceneTransferDynamicImpl(::SceneTH::weak::Scene input)
{
WIDGET_LOGI("sceneTransferDynamicImpl");
if (input.is_error()) {
WIDGET_LOGE("null input scene vtbl_ptr");
return 0;
}
taihe::optional<int64_t> implOp = input->getImpl();
if (!implOp.has_value()) {
WIDGET_LOGE("get SceneImpl failed");
return 0;
}
SceneImpl* si = reinterpret_cast<SceneImpl*>(implOp.value());
if (si == nullptr) {
WIDGET_LOGE("can't cast to SceneImpl");
return 0;
}
std::shared_ptr<SceneETS> internalScene = si->getInternalScene();
if (!internalScene) {
WIDGET_LOGE("get SceneETS failed");
return 0;
}
SCENE_NS::IScene::Ptr nativeObj = internalScene->GetNativeScene();
if (!nativeObj) {
WIDGET_LOGE("get IScene failed");
return 0;
}
napi_env jsenv;
if (!arkts_napi_scope_open(taihe::get_env(), &jsenv)) {
WIDGET_LOGE("arkts_napi_scope_open failed");
return 0;
}
if (!CheckNapiEnv(jsenv)) {
WIDGET_LOGE("CheckNapiEnv failed");
arkts_napi_scope_close_n(jsenv, 0, nullptr, nullptr);
return 0;
}
auto sceneJs = CreateFromNativeInstance(jsenv, nativeObj, PtrType::STRONG, {});
if (!sceneJs) {
WIDGET_LOGE("CreateFromNativeInstance failed");
arkts_napi_scope_close_n(jsenv, 0, nullptr, nullptr);
return 0;
}
napi_value sceneValue = sceneJs.ToNapiValue();
ani_ref resAny;
if (!arkts_napi_scope_close_n(jsenv, 1, &sceneValue, &resAny)) {
WIDGET_LOGE("arkts_napi_scope_close_n failed");
return 0;
}
return reinterpret_cast<uintptr_t>(resAny);
}
int64_t SceneImpl::getSceneNative()
{
if (!sceneETS_) {
return 0;
}
auto scene = sceneETS_->GetSceneAdapter();
return static_cast<int64_t>(reinterpret_cast<uintptr_t>(scene));
}
void SceneImpl::destroy()
{
if (sceneETS_) {
sceneETS_->Destroy();
sceneETS_.reset();
}
animations_ = std::nullopt;
}
::SceneNodes::NodeOrNull SceneImpl::getRoot()
{
if (!sceneETS_) {
return SceneNodes::NodeOrNull::make_nValue();
}
auto node = sceneETS_->GetRoot();
return SceneNodes::NodeOrNull::make_node(taihe::make_holder<NodeImpl, SceneNodes::Node>(node.value));
}
::SceneNodes::VariousNodesOrNull SceneImpl::getNodeByPath(
::taihe::string_view path, ::taihe::optional_view<::SceneNodes::NodeType> type)
{
if (!type || !type->is_valid()) {
WIDGET_LOGE("scene.getNodeByPath invalid node type");
}
std::shared_ptr<NodeETS> node = nullptr;
if (sceneETS_) {
node = sceneETS_->GetNodeByPath(std::string(path));
}
return NodeImpl::MakeVariousNodesOrNull(node);
}
::SceneTH::SceneComponent SceneImpl::createComponentSync(::SceneNodes::weak::Node node, ::taihe::string_view name)
{
if (!sceneETS_ || node.is_error()) {
taihe::set_error("Invalid parameters given");
return ::SceneTH::SceneComponent({nullptr, nullptr});
}
auto nodeOptional = static_cast<::SceneResources::weak::SceneResource>(node)->getImpl();
if (!nodeOptional.has_value()) {
taihe::set_error("invalid node in taihe object");
return ::SceneTH::SceneComponent({nullptr, nullptr});
}
auto nodeImpl = reinterpret_cast<NodeImpl*>(nodeOptional.value());
if (!nodeImpl) {
taihe::set_error("Invalid node in createComponent");
return ::SceneTH::SceneComponent({nullptr, nullptr});
}
auto component = sceneETS_->CreateComponent(nodeImpl->GetInternalNode(), std::string(name));
if (component) {
return taihe::make_holder<SceneComponentImpl, ::SceneTH::SceneComponent>(component.value);
} else {
taihe::set_error(component.error);
return ::SceneTH::SceneComponent({nullptr, nullptr});
}
}
::SceneTH::SceneComponentOrNull SceneImpl::getComponent(::SceneNodes::weak::Node node, ::taihe::string_view name)
{
if (!sceneETS_ || node.is_error()) {
return ::SceneTH::SceneComponentOrNull::make_nValue();
}
if (node.is_error()) {
return ::SceneTH::SceneComponentOrNull::make_nValue();
}
auto nodeOptional = static_cast<::SceneResources::weak::SceneResource>(node)->getImpl();
if (!nodeOptional.has_value()) {
WIDGET_LOGE("invalid node in taihe object");
return ::SceneTH::SceneComponentOrNull::make_nValue();
}
auto nodeImpl = reinterpret_cast<NodeImpl*>(nodeOptional.value());
if (!nodeImpl) {
WIDGET_LOGE("Invalid node in getComponent");
return ::SceneTH::SceneComponentOrNull::make_nValue();
}
auto component = sceneETS_->GetComponent(nodeImpl->GetInternalNode(), std::string(name));
if (component) {
auto holder = taihe::make_holder<SceneComponentImpl, ::SceneTH::SceneComponent>(component.value);
return ::SceneTH::SceneComponentOrNull::make_sc(holder);
} else {
WIDGET_LOGE("getComponent error: %s", component.error.c_str());
return ::SceneTH::SceneComponentOrNull::make_nValue();
}
}
::SceneNodes::VariousNodesOrNull SceneImpl::cloneNode(
::SceneNodes::weak::Node node, ::SceneNodes::weak::Node parent, ::taihe::string_view name)
{
WIDGET_LOGI("SceneImpl::cloneNode");
if (!sceneETS_) {
return ::SceneNodes::VariousNodesOrNull::make_nValue();
}
if (node.is_error()) {
return ::SceneNodes::VariousNodesOrNull::make_nValue();
}
auto nodeOptional = static_cast<::SceneResources::weak::SceneResource>(node)->getImpl();
if (!nodeOptional.has_value()) {
WIDGET_LOGE("invalid node in taihe object");
return ::SceneNodes::VariousNodesOrNull::make_nValue();
}
auto nodeImpl = reinterpret_cast<NodeImpl*>(nodeOptional.value());
if (!nodeImpl) {
WIDGET_LOGE("Invalid node in cloneNode");
return ::SceneNodes::VariousNodesOrNull::make_nValue();
}
if (parent.is_error()) {
return ::SceneNodes::VariousNodesOrNull::make_nValue();
}
auto parentNodeOptional = static_cast<::SceneResources::weak::SceneResource>(parent)->getImpl();
if (!parentNodeOptional.has_value()) {
WIDGET_LOGE("invalid parent in taihe object");
return ::SceneNodes::VariousNodesOrNull::make_nValue();
}
auto parentNodeImpl = reinterpret_cast<NodeImpl*>(parentNodeOptional.value());
if (!parentNodeImpl) {
WIDGET_LOGE("Invalid parent in cloneNode");
return ::SceneNodes::VariousNodesOrNull::make_nValue();
}
std::shared_ptr<NodeETS> clone =
sceneETS_->CloneNode(nodeImpl->GetInternalNode(), parentNodeImpl->GetInternalNode(), std::string(name));
return NodeImpl::MakeVariousNodesOrNull(clone);
}
::SceneTH::RenderConfiguration SceneImpl::getRenderConfiguration()
{
if (!sceneETS_) {
WIDGET_LOGE("getRenderConfiguration error: Empty SceneETS");
return taihe::make_holder<RenderConfigurationImpl, ::SceneTH::RenderConfiguration>(nullptr);
}
InvokeReturn<std::shared_ptr<RenderConfigurationETS>> rcETS = sceneETS_->GetRenderConfiguration();
if (rcETS) {
return taihe::make_holder<RenderConfigurationImpl, ::SceneTH::RenderConfiguration>(rcETS.value);
} else {
WIDGET_LOGE("getRenderConfiguration error: %s", rcETS.error.c_str());
return taihe::make_holder<RenderConfigurationImpl, ::SceneTH::RenderConfiguration>(nullptr);
}
}
::SceneTH::PCFConfig CreatePCFConfig()
{
std::shared_ptr<PCFConfigETS> pcfConfigETS = PCFConfigETS::Create();
return taihe::make_holder<PCFConfigImpl, ::SceneTH::PCFConfig>(pcfConfigETS);
}
}
using namespace OHOS::Render3D::KITETS;
TH_EXPORT_CPP_API_getDefaultRenderContext(getDefaultRenderContext);
TH_EXPORT_CPP_API_loadScene(loadScene);
TH_EXPORT_CPP_API_loadSceneWithLoadParams(::loadSceneWithLoadParams);
TH_EXPORT_CPP_API_sceneTransferStaticImpl(sceneTransferStaticImpl);
TH_EXPORT_CPP_API_sceneTransferDynamicImpl(sceneTransferDynamicImpl);
TH_EXPORT_CPP_API_CreatePCFConfig(CreatePCFConfig);