From 3276598e5575fe6adcdc9e07e17b31238111c387 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oskar=20Kwas=CC=81niewski?= Date: Tue, 29 Oct 2024 14:50:52 +0100 Subject: [PATCH 1/8] wip draco decoder --- cpp/react-native-draco.cpp | 144 +++++++++++++- cpp/react-native-draco.h | 26 ++- .../@react-native+codegen+0.76.0.patch | 91 +++++++++ example/src/App.tsx | 40 +++- src/DracoDecoderModule.ts | 175 ++++++++++++++++++ src/NativeDraco.ts | 134 +++++++++++++- src/index.tsx | 7 +- 7 files changed, 602 insertions(+), 15 deletions(-) create mode 100644 example/patches/@react-native+codegen+0.76.0.patch create mode 100644 src/DracoDecoderModule.ts diff --git a/cpp/react-native-draco.cpp b/cpp/react-native-draco.cpp index d74171e..bc3ea4a 100644 --- a/cpp/react-native-draco.cpp +++ b/cpp/react-native-draco.cpp @@ -3,12 +3,146 @@ namespace facebook::react { -ReactNativeDraco::ReactNativeDraco(std::shared_ptr jsInvoker) - : NativeDracoCxxSpec(std::move(jsInvoker)) {} +class DracoDecoder: public jsi::NativeState { +public: + DracoDecoder(): decoder_(std::make_unique()), last_status_(std::make_unique()) {} + + std::unique_ptr decoder_; + std::unique_ptr last_status_; +}; -double ReactNativeDraco::multiply(jsi::Runtime &rt, double a, double b) { - draco::DecoderBuffer buffer {}; - return a + b; +class DracoDecoderBuffer : public jsi::NativeState { +public: + DracoDecoderBuffer() : buffer_(std::make_unique()) {} + + std::unique_ptr buffer_; }; +class DracoPointCloud: public jsi::NativeState { +public: + DracoPointCloud(): pointCloud_(std::make_unique()) {} + + std::unique_ptr pointCloud_; +}; + +std::shared_ptr tryGetDecoder(jsi::Runtime& rt, jsi::Object& obj) { + if (!obj.hasNativeState(rt)) { + return nullptr; + } + + return std::dynamic_pointer_cast(obj.getNativeState(rt)); +} + +std::shared_ptr tryGetDecoderBuffer(jsi::Runtime& rt, jsi::Object& obj) { + if (!obj.hasNativeState(rt)) { + return nullptr; + } + + return std::dynamic_pointer_cast(obj.getNativeState(rt)); +} + +std::shared_ptr tryGetPointCloud(jsi::Runtime& rt, jsi::Object& obj) { + if (!obj.hasNativeState(rt)) { + return nullptr; + } + + return std::dynamic_pointer_cast(obj.getNativeState(rt)); +} + +ReactNativeDraco::ReactNativeDraco(std::shared_ptr jsInvoker) +: NativeDracoCxxSpec(std::move(jsInvoker)) {} + +jsi::Object ReactNativeDraco::createDecoderModule(jsi::Runtime &rt) { + jsi::Object dracoEncoder{rt}; + dracoEncoder.setNativeState(rt, std::make_shared()); + return dracoEncoder; +} + +jsi::Object ReactNativeDraco::createEncoderModule(jsi::Runtime &rt) { + return jsi::Object(rt); +} + +void ReactNativeDraco::attachPointCloudHandle(jsi::Runtime &rt, jsi::Object handle) { + handle.setNativeState(rt, std::make_shared()); +} + +bool ReactNativeDraco::GetAttributeDataArrayForAllPoints(jsi::Runtime &rt, jsi::Object decoderHandle, jsi::Object pointCloudHandle, jsi::Object pointAttributeHandle, NativeDracoDataType dataType, int outSize, jsi::Object outValues) { + return false; +} + +void ReactNativeDraco::SkipAttributeTransform(jsi::Runtime &rt, NativeDracoGeometryAttribute attributeType) { +} + +void ReactNativeDraco::initBuffer(jsi::Runtime &rt, jsi::Object bufferHandle, jsi::Object data, int length) { + auto dracoBuffer = std::make_shared(); + + if (!data.isArrayBuffer(rt)) { + throw jsi::JSError(rt, "Data needs to be an array buffer"); + } + + auto arrayBuffer = data.getArrayBuffer(rt); + auto bufferData = arrayBuffer.data(rt); + + + if (bufferData == nullptr) { + throw jsi::JSError(rt, "Buffer data is null"); + } + + dracoBuffer->buffer_->Init((const char*)bufferData, length); + bufferHandle.setNativeState(rt, dracoBuffer); +} + +jsi::Object ReactNativeDraco::GetAttributeByUniqueId(jsi::Runtime &rt, jsi::Object decoderHandle, jsi::Object pointCloudHandle, int uniqueId) { + return jsi::Object(rt); +} + +jsi::Object ReactNativeDraco::GetAttribute(jsi::Runtime &rt, jsi::Object decoderHandle, jsi::Object pointCloudHandle, int attributeId) { + return jsi::Object(rt); +} + +bool ReactNativeDraco::GetTrianglesUInt32Array(jsi::Runtime &rt, jsi::Object decoderHandle, jsi::Object meshHandle, int outSize, jsi::Object outValues) { + return false; +} + +NativeDracoStatus ReactNativeDraco::DecodeBufferToPointCloud(jsi::Runtime &rt, jsi::Object decoderHandle, jsi::Object bufferHandle, jsi::Object pointCloudHandle) { + auto decoder = tryGetDecoder(rt, decoderHandle); + auto buffer = tryGetDecoderBuffer(rt, bufferHandle); + auto pointCloud = tryGetPointCloud(rt, pointCloudHandle); + + auto status = decoder->decoder_->DecodeBufferToGeometry(buffer->buffer_.get(), pointCloud->pointCloud_.get()); + + + decoder->last_status_ = std::make_unique(std::move(status)); + + return Bridging::fromJs(rt, jsi::Value(status.code())); +} + +NativeDracoStatus ReactNativeDraco::DecodeBufferToMesh(jsi::Runtime &rt, jsi::Object decoderHandle, jsi::Object bufferHandle, jsi::Object meshHandle) { + return NativeDracoStatus::OK; +} + +int ReactNativeDraco::GetAttributeId(jsi::Runtime &rt, jsi::Object decoderHandle, jsi::Object pointCloudHandle, NativeDracoGeometryAttribute attributeType) { + return 0; +} + +NativeDracoEncodedGeometryType ReactNativeDraco::GetEncodedGeometryType_Deprecated(jsi::Runtime &rt, jsi::Object decoderHandle, jsi::Object inBuffer) { + auto decoderBuffer = tryGetDecoderBuffer(rt, inBuffer); + + if (decoderBuffer == nullptr) { + return NativeDracoEncodedGeometryType::INVALID_GEOMETRY_TYPE; + } + + auto value = draco::Decoder::GetEncodedGeometryType(decoderBuffer->buffer_.get()).value(); + + if (value == draco::INVALID_GEOMETRY_TYPE) { + return NativeDracoEncodedGeometryType::INVALID_GEOMETRY_TYPE; + } else if (value == draco::POINT_CLOUD) { + return NativeDracoEncodedGeometryType::POINT_CLOUD; + } else if (value == draco::TRIANGULAR_MESH) { + return NativeDracoEncodedGeometryType::TRIANGULAR_MESH; + } + + return NativeDracoEncodedGeometryType::INVALID_GEOMETRY_TYPE; +} + } // namespace facebook::react diff --git a/cpp/react-native-draco.h b/cpp/react-native-draco.h index 8d98c1e..688eca4 100644 --- a/cpp/react-native-draco.h +++ b/cpp/react-native-draco.h @@ -1,13 +1,37 @@ #pragma once #include +#include + +#include "draco/attributes/attribute_transform_type.h" +#include "draco/attributes/point_attribute.h" +#include "draco/compression/config/compression_shared.h" +#include "draco/compression/decode.h" +#include "draco/core/decoder_buffer.h" +#include "draco/mesh/mesh.h" namespace facebook::react { + + + class ReactNativeDraco: public NativeDracoCxxSpec { public: ReactNativeDraco(std::shared_ptr jsInvoker); + + NativeDracoEncodedGeometryType GetEncodedGeometryType_Deprecated(jsi::Runtime &rt, jsi::Object decoderHandle, jsi::Object inBuffer); - double multiply(jsi::Runtime &rt, double a, double b); + jsi::Object createDecoderModule(jsi::Runtime &rt); + jsi::Object createEncoderModule(jsi::Runtime &rt); + void attachPointCloudHandle(jsi::Runtime &rt, jsi::Object handle); + bool GetAttributeDataArrayForAllPoints(jsi::Runtime &rt, jsi::Object decoderHandle, jsi::Object pointCloudHandle, jsi::Object pointAttributeHandle, NativeDracoDataType dataType, int outSize, jsi::Object outValues); + void SkipAttributeTransform(jsi::Runtime &rt, NativeDracoGeometryAttribute attributeType); + jsi::Object GetAttributeByUniqueId(jsi::Runtime &rt, jsi::Object decoderHandle, jsi::Object pointCloudHandle, int uniqueId); + jsi::Object GetAttribute(jsi::Runtime &rt, jsi::Object decoderHandle, jsi::Object pointCloudHandle, int attributeId); + bool GetTrianglesUInt32Array(jsi::Runtime &rt, jsi::Object decoderHandle, jsi::Object meshHandle, int outSize, jsi::Object outValues); + NativeDracoStatus DecodeBufferToPointCloud(jsi::Runtime &rt, jsi::Object decoderHandle, jsi::Object bufferHandle, jsi::Object pointCloudHandle); + NativeDracoStatus DecodeBufferToMesh(jsi::Runtime &rt, jsi::Object decoderHandle, jsi::Object bufferHandle, jsi::Object meshHandle); + int GetAttributeId(jsi::Runtime &rt, jsi::Object decoderHandle, jsi::Object pointCloudHandle, NativeDracoGeometryAttribute attributeType); + void initBuffer(jsi::Runtime &rt, jsi::Object bufferHandle, jsi::Object data, int length); }; } diff --git a/example/patches/@react-native+codegen+0.76.0.patch b/example/patches/@react-native+codegen+0.76.0.patch new file mode 100644 index 0000000..d3734d9 --- /dev/null +++ b/example/patches/@react-native+codegen+0.76.0.patch @@ -0,0 +1,91 @@ +diff --git a/node_modules/@react-native/codegen/lib/generators/modules/GenerateModuleH.js b/node_modules/@react-native/codegen/lib/generators/modules/GenerateModuleH.js +index 13232a1..fe52764 100644 +--- a/node_modules/@react-native/codegen/lib/generators/modules/GenerateModuleH.js ++++ b/node_modules/@react-native/codegen/lib/generators/modules/GenerateModuleH.js +@@ -424,7 +424,9 @@ function generateEnum(hasteModuleName, origEnumName, members, memberType) { + const nativeEnumMemberType = + memberType === 'StringTypeAnnotation' ? 'std::string' : 'int32_t'; + const getMemberValueAppearance = value => +- memberType === 'StringTypeAnnotation' ? `"${value}"` : `${value}`; ++ { ++ return memberType === 'StringTypeAnnotation' ? `"${value}"` : `${value}`; ++ }; + const fromCases = + members + .map( +@@ -458,6 +460,7 @@ function generateEnum(hasteModuleName, origEnumName, members, memberType) { + function createEnums(hasteModuleName, enumMap, resolveAlias) { + return Object.entries(enumMap) + .map(([enumName, enumNode]) => { ++ console.log(enumMap) + return generateEnum( + hasteModuleName, + enumName, +diff --git a/node_modules/@react-native/codegen/lib/parsers/typescript/parser.js b/node_modules/@react-native/codegen/lib/parsers/typescript/parser.js +index da4afe3..69bbcb1 100644 +--- a/node_modules/@react-native/codegen/lib/parsers/typescript/parser.js ++++ b/node_modules/@react-native/codegen/lib/parsers/typescript/parser.js +@@ -174,12 +174,22 @@ class TypeScriptParser { + _typeAnnotation$membe === void 0 + ? void 0 + : _typeAnnotation$membe.initializer; +- const enumMembersType = ++ let enumMembersType = + !enumInitializer || enumInitializer.type === 'StringLiteral' + ? 'StringTypeAnnotation' + : enumInitializer.type === 'NumericLiteral' + ? 'NumberTypeAnnotation' + : null; ++ ++ // Handle case where enum is negative numbers ++ ++ if (enumMembersType === null) { ++ const isNegative = enumInitializer.type = 'UnaryExpression' && enumInitializer.operator === '-'; ++ if (isNegative) { ++ enumMembersType = 'NumberTypeAnnotation'; ++ } ++ } ++ + if (!enumMembersType) { + throw new Error( + 'Enum values must be either blank, number, or string values.', +@@ -194,9 +204,10 @@ class TypeScriptParser { + const enumInitializerType = + enumMembersType === 'StringTypeAnnotation' + ? 'StringLiteral' +- : enumMembersType === 'NumberTypeAnnotation' ++ : (enumMembersType === 'NumberTypeAnnotation' || enumMembersType === 'UnaryExpression') + ? 'NumericLiteral' + : null; ++ + typeAnnotation.members.forEach(member => { + var _member$initializer$t, _member$initializer; + if ( +@@ -209,6 +220,11 @@ class TypeScriptParser { + ? _member$initializer$t + : 'StringLiteral') !== enumInitializerType + ) { ++ if (member.initializer?.argument.type === 'NumericLiteral') { ++ return; ++ } ++ ++ console.log(member.initializer); + throw new Error( + 'Enum values can not be mixed. They all must be either blank, number, or string values.', + ); +@@ -218,6 +234,15 @@ class TypeScriptParser { + parseEnumMembers(typeAnnotation) { + return typeAnnotation.members.map(member => { + var _member$initializer$v, _member$initializer2; ++ console.log("PARSE ENUM MEMBERS", member.id.name, member.initializer.type, member.initializer.value); ++ if (member.initializer.operator == '-') { ++ console.log('NEGATIVE NUMBER'); ++ console.log(member.id.name, -member.initializer.argument.value); ++ return { ++ name: member.id.name, ++ value: -member.initializer.argument.value ++ } ++ } + return { + name: member.id.name, + value: diff --git a/example/src/App.tsx b/example/src/App.tsx index 6e6840e..cad66c9 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -1,11 +1,43 @@ -import { StyleSheet, View, Text } from 'react-native'; -import { multiply } from 'react-native-draco'; +import { StyleSheet, View, Text, Button } from 'react-native'; +import { DracoDecoderModule } from 'react-native-draco'; export default function App() { - const result = multiply(5, 2); + const decode = () => { + const byteArray = new Uint8Array(0); + // Create the Draco decoder. + const decoderModule = DracoDecoderModule(); + const buffer = new decoderModule.DecoderBuffer(); + buffer.Init(byteArray, byteArray.length); + + // Create a buffer to hold the encoded data. + const decoder = new decoderModule.Decoder(); + const geometryType = decoder.GetEncodedGeometryType(buffer); + console.log({ geometryType }); + + // Decode the encoded geometry. + let outputGeometry; + let status; + if (geometryType === decoderModule.TRIANGULAR_MESH) { + outputGeometry = new decoderModule.Mesh(); + status = decoder.DecodeBufferToMesh(buffer, outputGeometry); + } else { + outputGeometry = new decoderModule.PointCloud(); + status = decoder.DecodeBufferToPointCloud(buffer, outputGeometry); + } + + console.log(status); + + // You must explicitly delete objects created from the DracoDecoderModule + // or Decoder. + decoderModule.destroy(outputGeometry); + decoderModule.destroy(decoder); + decoderModule.destroy(buffer); + }; + return ( - Result: {result} + Draco Example +