1 /*
2 * Copyright (C) 2025 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "geometry_definition/CustomJS.h"
17
18 #include <ColorProxy.h>
19 #include <Vec2Proxy.h>
20 #include <Vec3Proxy.h>
21 #include <napi_api.h>
22 #include <scene/interface/intf_create_mesh.h>
23
24 namespace GeometryDefinition {
25
CustomJS()26 CustomJS::CustomJS() : GeometryDefinition() {}
27
Init(napi_env env,napi_value exports)28 void CustomJS::Init(napi_env env, napi_value exports)
29 {
30 auto ctor = [](napi_env env, napi_callback_info info) -> napi_value {
31 napi_value self;
32 napi_get_cb_info(env, info, nullptr, nullptr, &self, nullptr);
33 auto selfObj = NapiApi::Object { env, self };
34 for (auto&& propertyName : { "vertices", "indices", "normals", "uvs", "colors" }) {
35 napi_value array;
36 napi_create_array(env, &array);
37 const auto arrayObj = NapiApi::Object(env, array);
38 selfObj.Set(propertyName, arrayObj);
39 }
40 return self;
41 };
42
43 auto getType = [](napi_env e, napi_callback_info) { return NapiApi::Env { e }.GetNumber(GeometryType::CUSTOM); };
44 napi_value topology;
45 napi_create_uint32(env, PrimitiveTopology::TRIANGLE_LIST, &topology);
46 napi_value undefined;
47 napi_get_undefined(env, &undefined);
48 const auto props = BASE_NS::vector<napi_property_descriptor> {
49 // clang-format off
50 { "geometryType", nullptr, nullptr, getType, nullptr, nullptr, napi_default_jsproperty, nullptr },
51 { "topology", nullptr, nullptr, nullptr, nullptr, topology, napi_default_jsproperty, nullptr },
52 { "vertices", nullptr, nullptr, nullptr, nullptr, undefined, napi_default_jsproperty, nullptr },
53 { "indices", nullptr, nullptr, nullptr, nullptr, undefined, napi_default_jsproperty, nullptr },
54 { "normals", nullptr, nullptr, nullptr, nullptr, undefined, napi_default_jsproperty, nullptr },
55 { "uvs", nullptr, nullptr, nullptr, nullptr, undefined, napi_default_jsproperty, nullptr },
56 { "colors", nullptr, nullptr, nullptr, nullptr, undefined, napi_default_jsproperty, nullptr },
57 // clang-format on
58 };
59 DefineClass(env, exports, "CustomGeometry", props, ctor);
60 }
61
RegisterEnums(NapiApi::Object exports)62 void CustomJS::RegisterEnums(NapiApi::Object exports)
63 {
64 napi_value v;
65 NapiApi::Object PrimitiveTopology(exports.GetEnv());
66
67 #define DECL_ENUM(enu, x) \
68 { \
69 napi_create_uint32(enu.GetEnv(), PrimitiveTopology::x, &v); \
70 enu.Set(#x, v); \
71 }
72 DECL_ENUM(PrimitiveTopology, TRIANGLE_LIST);
73 DECL_ENUM(PrimitiveTopology, TRIANGLE_STRIP);
74 #undef DECL_ENUM
75
76 exports.Set("PrimitiveTopology", PrimitiveTopology);
77 }
78
FromJs(NapiApi::Object jsDefinition)79 GeometryDefinition* CustomJS::FromJs(NapiApi::Object jsDefinition)
80 {
81 auto newObj = new CustomJS();
82 if (newObj->SetTopology(jsDefinition)
83 // clang-format off
84 && ArrayToNative(jsDefinition, "vertices", newObj->vertices_)
85 && ArrayToNative(jsDefinition, "indices", newObj->indices_)
86 && ArrayToNative(jsDefinition, "normals", newObj->normals_)
87 && ArrayToNative(jsDefinition, "uvs", newObj->uvs_)
88 && ArrayToNative(jsDefinition, "colors", newObj->colors_)
89 // clang-format on
90 ) {
91 return newObj;
92 } else {
93 delete newObj;
94 LOG_E("Unable to create CustomJS: Invalid JS object given");
95 }
96 return {};
97 }
98
CreateMesh(const SCENE_NS::ICreateMesh::Ptr & creator,const SCENE_NS::MeshConfig & config) const99 SCENE_NS::IMesh::Ptr CustomJS::CreateMesh(
100 const SCENE_NS::ICreateMesh::Ptr& creator, const SCENE_NS::MeshConfig& config) const
101 {
102 const auto meshData = SCENE_NS::CustomMeshData { GetTopology(), vertices_, indices_, normals_, uvs_, colors_ };
103 return creator->Create(config, meshData).GetResult();
104 }
105
GetTopology() const106 SCENE_NS::PrimitiveTopology CustomJS::GetTopology() const
107 {
108 if (topology_ == PrimitiveTopology::TRIANGLE_LIST) {
109 return SCENE_NS::PrimitiveTopology::TRIANGLE_LIST;
110 } else {
111 return SCENE_NS::PrimitiveTopology::TRIANGLE_STRIP;
112 }
113 }
114
SetTopology(NapiApi::Object & jsDefinition)115 bool CustomJS::SetTopology(NapiApi::Object& jsDefinition)
116 {
117 if (auto value = jsDefinition.Get<uint32_t>("topology"); value.IsValid()) {
118 uint32_t newTopology = value;
119 if (newTopology <= PrimitiveTopology::TRIANGLE_STRIP) {
120 topology_ = static_cast<PrimitiveTopology>(newTopology);
121 return true;
122 }
123 }
124 LOG_E("Invalid primitive topology given to CustomGeometry");
125 return false;
126 }
127
128 template<typename ItemType>
ArrayToNative(NapiApi::Object & obj,const BASE_NS::string & arrayName,BASE_NS::vector<ItemType> & target)129 bool CustomJS::ArrayToNative(NapiApi::Object& obj, const BASE_NS::string& arrayName, BASE_NS::vector<ItemType>& target)
130 {
131 NapiApi::Array jsArray = obj.Get<NapiApi::Array>(arrayName);
132 auto newItems = BASE_NS::vector<ItemType> {};
133 const auto length = jsArray.Count();
134 newItems.reserve(length);
135 for (auto i = 0; i < length; i++) {
136 const auto jsItem = jsArray.Get_value(i);
137 if (!jsItem) {
138 LOG_E("Invalid array given to CustomGeometry");
139 return false;
140 }
141 auto conversionOk { false };
142 newItems.emplace_back(ToNative<ItemType>(obj.GetEnv(), jsItem, conversionOk));
143 if (!conversionOk) {
144 LOG_E("Invalid array given to CustomGeometry");
145 return false;
146 }
147 }
148 target.swap(newItems);
149 return true;
150 }
151
152 template<>
ToNative(napi_env env,napi_value jsItem,bool & conversionOk)153 uint32_t CustomJS::ToNative(napi_env env, napi_value jsItem, bool& conversionOk)
154 {
155 auto tmpSigned = int64_t {};
156 conversionOk = (napi_get_value_int64(env, jsItem, &tmpSigned) == napi_ok);
157 conversionOk &= 0 <= tmpSigned && tmpSigned <= UINT32_MAX;
158 return static_cast<uint32_t>(tmpSigned);
159 }
160
161 template<>
ToNative(napi_env env,napi_value jsItem,bool & conversionOk)162 BASE_NS::Math::Vec2 CustomJS::ToNative(napi_env env, napi_value jsItem, bool& conversionOk)
163 {
164 return Vec2Proxy::ToNative(NapiApi::Object { env, jsItem }, conversionOk);
165 }
166
167 template<>
ToNative(napi_env env,napi_value jsItem,bool & conversionOk)168 BASE_NS::Math::Vec3 CustomJS::ToNative(napi_env env, napi_value jsItem, bool& conversionOk)
169 {
170 return Vec3Proxy::ToNative(NapiApi::Object { env, jsItem }, conversionOk);
171 }
172
173 template<>
ToNative(napi_env env,napi_value jsItem,bool & conversionOk)174 BASE_NS::Color CustomJS::ToNative(napi_env env, napi_value jsItem, bool& conversionOk)
175 {
176 return ColorProxy::ToNative(NapiApi::Object { env, jsItem }, conversionOk);
177 }
178
179 } // namespace GeometryDefinition
180