* 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 "CameraImpl.h"
#ifdef __SCENE_ADAPTER__
#include "3d_widget_adapter_log.h"
#endif
#include "ColorImpl.h"
#include "EffectImpl.h"
#include "Vec3Impl.h"
#include "Vec4Impl.h"
#include "interop_js/arkts_interop_js_api.h"
#include "interop_js/arkts_esvalue.h"
#include "CheckNapiEnv.h"
#include "BaseObjectJS.h"
namespace OHOS::Render3D::KITETS {
CameraImpl::CameraImpl(const std::shared_ptr<CameraETS> cameraETS) : NodeImpl(cameraETS), cameraETS_(cameraETS)
{}
CameraImpl::~CameraImpl()
{
cameraETS_.reset();
}
double CameraImpl::getFov()
{
if (cameraETS_) {
return cameraETS_->GetFov();
} else {
return 0.0F;
}
}
void CameraImpl::setFov(double fov)
{
if (cameraETS_) {
cameraETS_->SetFov(fov);
}
}
double CameraImpl::getNearPlane()
{
if (cameraETS_) {
return cameraETS_->GetNear();
} else {
return 0.0F;
}
}
void CameraImpl::setNearPlane(double nearPlane)
{
if (cameraETS_) {
cameraETS_->SetNear(nearPlane);
}
}
double CameraImpl::getFarPlane()
{
if (cameraETS_) {
return cameraETS_->GetFar();
} else {
return 0.0F;
}
}
void CameraImpl::setFarPlane(double farPlane)
{
if (cameraETS_) {
cameraETS_->SetFar(farPlane);
}
}
bool CameraImpl::getEnabled()
{
if (cameraETS_) {
return cameraETS_->GetEnabled();
} else {
return false;
}
}
void CameraImpl::setEnabled(bool enabled)
{
if (cameraETS_) {
cameraETS_->SetEnabled(enabled);
}
}
::SceneNodes::PostProcessSettingsOrNull CameraImpl::getPostProcess()
{
if (cameraETS_) {
std::shared_ptr<PostProcessETS> pp = cameraETS_->GetPostProcess();
if (pp) {
auto pps = taihe::make_holder<PostProcessSettingsImpl, ScenePostProcessSettings::PostProcessSettings>(pp);
return SceneNodes::PostProcessSettingsOrNull::make_postProcess(pps);
}
}
return SceneNodes::PostProcessSettingsOrNull::make_nValue();
}
void CameraImpl::setPostProcess(::SceneNodes::PostProcessSettingsOrNull const& process)
{
if (!cameraETS_) {
WIDGET_LOGE("The camera is null when set post process settings");
return;
}
if (process.holds_nValue()) {
WIDGET_LOGI("Set post process to null");
cameraETS_->SetPostProcess(nullptr);
return;
}
ScenePostProcessSettings::PostProcessSettings ppValue = process.get_postProcess_ref();
taihe::optional<ScenePostProcessSettings::ToneMappingSettings> tonemapOp = ppValue->getToneMapping();
std::shared_ptr<TonemapETS> tonemap;
if (tonemapOp.has_value()) {
tonemap = ToneMappingSettingsImpl::CreateInternal(tonemapOp.value());
} else {
WIDGET_LOGI("CameraImpl::setPostProcess: disable tonemap");
tonemap = nullptr;
}
taihe::optional<ScenePostProcessSettings::BloomSettings> bloomOp = ppValue->getBloom();
std::shared_ptr<BloomETS> bloom;
if (bloomOp.has_value()) {
bloom = BloomSettingsImpl::CreateInternal(bloomOp.value());
} else {
WIDGET_LOGI("CameraImpl::setPostProcess: disable bloom");
bloom = nullptr;
}
taihe::optional<ScenePostProcessSettings::VignetteSettings> vignetteOp = ppValue->getVignette();
std::shared_ptr<VignetteETS> vignette;
if (vignetteOp.has_value()) {
vignette = VignetteSettingsImpl::CreateInternal(vignetteOp.value());
} else {
WIDGET_LOGI("CameraImpl::setPostProcess: disable vignette");
vignette = nullptr;
}
taihe::optional<ScenePostProcessSettings::ColorFringeSettings> colorFringeOp = ppValue->getColorFringe();
std::shared_ptr<ColorFringeETS> colorFringe;
if (colorFringeOp.has_value()) {
colorFringe = ColorFringeSettingsImpl::CreateInternal(colorFringeOp.value());
} else {
WIDGET_LOGI("CameraImpl::setPostProcess: disable colorFringe");
colorFringe = nullptr;
}
WIDGET_LOGI("Create new post process settings");
std::shared_ptr<PostProcessETS> pp = std::make_shared<PostProcessETS>(tonemap, bloom, vignette, colorFringe);
cameraETS_->SetPostProcess(pp);
}
::SceneNodes::ColorOrNull CameraImpl::getClearColor()
{
if (!cameraETS_) {
return SceneNodes::ColorOrNull::make_nValue();
}
InvokeReturn<std::shared_ptr<Vec4Proxy>> color = cameraETS_->GetClearColor();
if (color.error.empty()) {
SceneTypes::Color sc = taihe::make_holder<ColorImpl, SceneTypes::Color>(color.value);
return SceneNodes::ColorOrNull::make_color(sc);
} else {
return SceneNodes::ColorOrNull::make_nValue();
}
}
void CameraImpl::setClearColor(::SceneNodes::ColorOrNull const& color)
{
if (cameraETS_) {
if (color.holds_color()) {
const SceneTypes::Color colorValue = color.get_color_ref();
cameraETS_->SetClearColor(true,
BASE_NS::Math::Vec4(colorValue->getR(), colorValue->getG(), colorValue->getB(), colorValue->getA()));
} else {
cameraETS_->SetClearColor(false, BASE_NS::Math::ZERO_VEC4);
}
}
}
::taihe::optional<bool> CameraImpl::getMsaa()
{
if (!cameraETS_) {
return ::taihe::optional<bool>(std::in_place, false);
}
return ::taihe::optional<bool>(std::in_place, cameraETS_->GetMSAA());
}
void CameraImpl::setMsaa(::taihe::optional_view<bool> msaa)
{
if (!cameraETS_) {
return;
}
if (msaa) {
cameraETS_->SetMSAA(msaa.value());
} else {
cameraETS_->SetMSAA(false);
}
}
::taihe::optional<::SceneTypes::RenderingPipelineType> CameraImpl::getRenderingPipeline()
{
SceneTypes::RenderingPipelineType type = SceneTypes::RenderingPipelineType::key_t::FORWARD_LIGHTWEIGHT;
if (cameraETS_) {
type = SceneTypes::RenderingPipelineType::from_value(static_cast<int32_t>(cameraETS_->GetRenderingPipeline()));
}
return taihe::optional<::SceneTypes::RenderingPipelineType>(std::in_place, type);
}
void CameraImpl::setRenderingPipeline(::taihe::optional_view<::SceneTypes::RenderingPipelineType> renderingPipeline)
{
if (cameraETS_) {
SceneTypes::RenderingPipelineType type = SceneTypes::RenderingPipelineType::key_t::FORWARD_LIGHTWEIGHT;
if (renderingPipeline.has_value()) {
type = renderingPipeline.value();
}
cameraETS_->SetRenderingPipeline((CameraETS::RenderingPipelineType)type.get_value());
}
}
::SceneNodes::EffectsContainer CameraImpl::getEffectsContainer()
{
return taihe::make_holder<EffectsContainerImpl, ::SceneNodes::EffectsContainer>(
std::make_shared<EffectsContainerETS>(cameraETS_->GetEffectsContainer()));
}
::taihe::array<::SceneTH::RaycastResult> CameraImpl::raycastSync(
::SceneTypes::weak::Vec2 viewPosition, ::SceneTH::RaycastParameters const& params)
{
if (!cameraETS_) {
taihe::set_error("Invalid camera");
return {};
}
BASE_NS::Math::Vec2 vp(viewPosition->getX(), viewPosition->getY());
taihe::optional<SceneNodes::Node> rootNodeOp = params.rootNode;
std::shared_ptr<NodeETS> rootNode{nullptr};
if (rootNodeOp.has_value()) {
auto nodeOptional = static_cast<::SceneResources::SceneResource>(rootNodeOp.value())->getImpl();
if (!nodeOptional.has_value()) {
taihe::set_error("invalid node in taihe object");
return {};
}
auto ni = reinterpret_cast<NodeImpl*>(nodeOptional.value());
if (ni != nullptr) {
rootNode = ni->GetInternalNode();
}
}
InvokeReturn<std::vector<CameraETS::RaycastResult>> result = cameraETS_->Raycast(vp, rootNode);
if (result.error.empty()) {
std::vector<CameraETS::RaycastResult> rrv = result.value;
std::vector<::SceneTH::RaycastResult> rrVector;
rrVector.reserve(rrv.size());
for (size_t index = 0; index < rrv.size(); ++index) {
rrVector.emplace_back(::SceneTH::RaycastResult{NodeImpl::MakeVariousNodes(rrv[index].node),
rrv[index].centerDistance,
taihe::make_holder<Vec3Impl, SceneTypes::Vec3>(rrv[index].hitPosition)});
}
return taihe::array_view<::SceneTH::RaycastResult>(rrVector);
} else {
taihe::set_error(result.error);
return {};
}
}
::SceneTypes::Vec3 CameraImpl::worldToScreen(::SceneTypes::weak::Vec3 worldPosition)
{
if (!cameraETS_) {
WIDGET_LOGE("cameraETS_ is null");
return SceneTypes::Vec3({nullptr, nullptr});
}
BASE_NS::Math::Vec3 world{worldPosition->getX(), worldPosition->getY(), worldPosition->getZ()};
return taihe::make_holder<Vec3Impl, ::SceneTypes::Vec3>(cameraETS_->WorldToScreen(world));
}
::SceneTypes::Vec3 CameraImpl::screenToWorld(::SceneTypes::weak::Vec3 viewPosition)
{
if (!cameraETS_) {
WIDGET_LOGE("cameraETS_ is null");
return SceneTypes::Vec3({nullptr, nullptr});
}
BASE_NS::Math::Vec3 screen{viewPosition->getX(), viewPosition->getY(), viewPosition->getZ()};
return taihe::make_holder<Vec3Impl, ::SceneTypes::Vec3>(cameraETS_->ScreenToWorld(screen));
}
::SceneTypes::Mat4x4 CameraImpl::getViewMatrix()
{
if (cameraETS_) {
BASE_NS::Math::Mat4X4 viewMatrix = cameraETS_->GetViewMatrix();
return SceneTypes::Mat4x4{taihe::make_holder<Vec4Impl, SceneTypes::Vec4>(viewMatrix[0]),
taihe::make_holder<Vec4Impl, SceneTypes::Vec4>(viewMatrix[1]),
taihe::make_holder<Vec4Impl, SceneTypes::Vec4>(viewMatrix[2]),
taihe::make_holder<Vec4Impl, SceneTypes::Vec4>(viewMatrix[3])};
}
return SceneTypes::Mat4x4{taihe::make_holder<Vec4Impl, SceneTypes::Vec4>(BASE_NS::Math::ZERO_VEC4),
taihe::make_holder<Vec4Impl, SceneTypes::Vec4>(BASE_NS::Math::ZERO_VEC4),
taihe::make_holder<Vec4Impl, SceneTypes::Vec4>(BASE_NS::Math::ZERO_VEC4),
taihe::make_holder<Vec4Impl, SceneTypes::Vec4>(BASE_NS::Math::ZERO_VEC4)};
}
::SceneTypes::Mat4x4 CameraImpl::getProjectionMatrix()
{
if (cameraETS_) {
BASE_NS::Math::Mat4X4 projectionMatrix = cameraETS_->GetProjectionMatrix();
return SceneTypes::Mat4x4{taihe::make_holder<Vec4Impl, SceneTypes::Vec4>(projectionMatrix[0]),
taihe::make_holder<Vec4Impl, SceneTypes::Vec4>(projectionMatrix[1]),
taihe::make_holder<Vec4Impl, SceneTypes::Vec4>(projectionMatrix[2]),
taihe::make_holder<Vec4Impl, SceneTypes::Vec4>(projectionMatrix[3])};
}
return SceneTypes::Mat4x4{taihe::make_holder<Vec4Impl, SceneTypes::Vec4>(BASE_NS::Math::ZERO_VEC4),
taihe::make_holder<Vec4Impl, SceneTypes::Vec4>(BASE_NS::Math::ZERO_VEC4),
taihe::make_holder<Vec4Impl, SceneTypes::Vec4>(BASE_NS::Math::ZERO_VEC4),
taihe::make_holder<Vec4Impl, SceneTypes::Vec4>(BASE_NS::Math::ZERO_VEC4)};
}
::SceneNodes::Camera cameraTransferStaticImpl(uintptr_t input)
{
WIDGET_LOGI("cameraTransferStaticImpl");
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 SceneNodes::Camera({nullptr, nullptr});
}
TrueRootObject* tro = reinterpret_cast<TrueRootObject*>(nativePtr);
if (tro == nullptr) {
WIDGET_LOGE("transfer camera failed");
return SceneNodes::Camera({nullptr, nullptr});
}
SCENE_NS::ICamera::Ptr cam = tro->GetNativeObject<SCENE_NS::ICamera>();
return taihe::make_holder<CameraImpl, ::SceneNodes::Camera>(std::make_shared<CameraETS>(cam));
}
uintptr_t cameraTransferDynamicImpl(::SceneNodes::weak::Camera input)
{
WIDGET_LOGI("cameraTransferDynamicImpl");
RETURN_IF_NULL_WITH_VALUE(!input.is_error(), 0);
taihe::optional<int64_t> implOp = static_cast<::SceneResources::weak::SceneResource>(input)->getImpl();
RETURN_IF_NULL_WITH_VALUE(implOp.has_value(), 0);
CameraImpl* camera = reinterpret_cast<CameraImpl*>(implOp.value());
RETURN_IF_NULL_WITH_VALUE(camera, 0);
std::shared_ptr<CameraETS> internalCamera = camera->getInternalCamera();
RETURN_IF_NULL_WITH_VALUE(internalCamera, 0);
META_NS::IObject::Ptr nativeObj = internalCamera->GetNativeObj();
RETURN_IF_NULL_WITH_VALUE(nativeObj, 0);
SCENE_NS::INode::Ptr cameraNode = interface_pointer_cast<SCENE_NS::INode>(nativeObj);
RETURN_IF_NULL_WITH_VALUE(cameraNode, 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, cameraNode->GetScene(), PtrType::STRONG, {});
if (!sceneJs) {
WIDGET_LOGE("create SceneJS failed.");
arkts_napi_scope_close_n(jsenv, 0, nullptr, nullptr);
return 0;
}
auto nativeCamera = interface_cast<SCENE_NS::ICamera>(nativeObj);
auto postProc = META_NS::GetValue(nativeCamera->PostProcess());
napi_value nullValue;
napi_get_null(jsenv, &nullValue);
napi_value args[] = {sceneJs.ToNapiValue(), nullValue};
auto cameraObj = CreateFromNativeInstance(jsenv, cameraNode, PtrType::WEAK, args);
if (!cameraObj) {
WIDGET_LOGE("create CameraJS failed.");
arkts_napi_scope_close_n(jsenv, 0, nullptr, nullptr);
return 0;
}
if (postProc) {
napi_value args[] = {cameraObj.ToNapiValue(), nullValue};
auto postProcJS = CreateFromNativeInstance(jsenv, postProc, PtrType::WEAK, args);
cameraObj.Set("postProcess", postProcJS);
}
napi_value cameraValue = cameraObj.ToNapiValue();
ani_ref resAny;
if (!arkts_napi_scope_close_n(jsenv, 1, &cameraValue, &resAny)) {
WIDGET_LOGE("arkts_napi_scope_close_n failed");
return 0;
}
return reinterpret_cast<uintptr_t>(resAny);
}
}
using namespace OHOS::Render3D::KITETS;
TH_EXPORT_CPP_API_cameraTransferStaticImpl(cameraTransferStaticImpl);
TH_EXPORT_CPP_API_cameraTransferDynamicImpl(cameraTransferDynamicImpl);