diff --git a/CHANGELOG.md b/CHANGELOG.md index 8cd02fd3e..2f46fdc14 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -98,6 +98,8 @@ - **Android: a failure while a recording is being stopped no longer leaves `getRecordingStatus()` at IOError (`ARSessionRecorder`).** `encoder.stop()` and `muxer.stop()` failures reported it to the scene after the stop had already set the status back to None, so the status read IOError with nothing recording. Such a failure is now reported in the stop's result only. `ARScene.stopRecording()` may be called off the UI thread. - **Android hit tests always deliver their results (`VRORendererARCore_JNI`, `VRORenderer_JNI`).** The AR point, ray and position hit tests, the Quest OpenXR hit tests and the scene hit test kept the caller's one-shot listener only as a weak global reference, so the garbage collector could take it before the renderer thread answered, and the results never arrived. It is now a global reference, deleted once the callback has run. - **iOS downloads from a signed URL are saved (`VROPlatformDownloadURLToFile`, `VROPlatformDownloadURLToFileAsync`).** The temporary file was named after the URL's last path component with its query, and a signed URL's token pushed the name past the 255-byte file name limit, so the write failed and the download still reported success with a file that did not exist. `VROPlatformLastPathComponent` drops the query and fragment, and a failed write now fails the download. +- **Android: a camera added before the renderer no longer crashes (`VROPlatformUtil`, #597).** `libviro_renderer.so` now sets the `JavaVM` in `JNI_OnLoad`. It was set only when a renderer or loader started, and under Fabric `ViroARCamera`'s view is built first, so adding its node reached `getJNIEnv` with no VM. +- **A GLB whose skins share joints animates every skin (`VROGLTFLoader`, #599).** Resampling runs once per skin and rewrote a shared joint's channels in place; the next skin's pass kept only the translation of the merged channel, so those joints lost their rotation and scale. Every property of a channel is now copied. ### Removed diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 45aa965c8..aab292e5c 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -36,6 +36,8 @@ - **Co-location:** `VROColocationSession::deviceId()`, sent as `x-rv-device`, so the relay bills a device once. - **A click goes to the node drawn on top,** and a click in open space reaches the controller's own delegates again. - **Text sits where its vertical alignment puts it,** and a back-culled draw after a front-culled one culls back faces again. +- **GLB characters with one skin per mesh animate every part,** not only the first skin's. +- **Android: a `ViroARCamera` no longer crashes the scene as it mounts.** - **Video:** a paused iOS video stays paused and its late callbacks no longer reach a freed texture; Android play and pause keep their order. - **iOS:** ViroKit targets iOS 15.1, and builds for the iOS Simulator (arm64 and x86_64) as well as the device. The copy into viro now makes `ViroKit.xcframework`, replacing only the slice just built. `ios/build_ios_simulator_libs.sh` adds the simulator builds of LinearMath, protobuf-lite and GVR Audio that ViroKit links. - **Removed:** the macOS target, which no longer linked and nothing built, and dead code (an unused metrics client, the API key validator, the OpenCV headers, an old OpenXR loader). diff --git a/ViroRenderer/VROGLTFLoader.cpp b/ViroRenderer/VROGLTFLoader.cpp index 606a51940..95868d06c 100644 --- a/ViroRenderer/VROGLTFLoader.cpp +++ b/ViroRenderer/VROGLTFLoader.cpp @@ -1516,19 +1516,25 @@ void VROGLTFLoader::resampleSkeletalChannelsToCommonGrid(std::map_hasTranslation) { hasTrans = true; mergedFrames[gi]->translation = step ? fA->translation : fA->translation.interpolate(fB->translation, alpha); - } else if (chan->_hasScale) { + } + if (chan->_hasScale) { hasScale = true; mergedFrames[gi]->scale = step ? fA->scale : fA->scale.interpolate(fB->scale, alpha); - } else if (chan->_hasRotation) { + } + if (chan->_hasRotation) { hasRotation = true; mergedFrames[gi]->rotation = step ? fA->rotation : VROQuaternion::slerp(fA->rotation, fB->rotation, alpha); - } else if (chan->_hasMorphWeights) { + } + if (chan->_hasMorphWeights) { hasMorph = true; std::map weights; for (const auto &kv : fA->morphWeights) { diff --git a/ViroRenderer/VROPlatformUtil.cpp b/ViroRenderer/VROPlatformUtil.cpp index a28a811f8..8ae3ea453 100644 --- a/ViroRenderer/VROPlatformUtil.cpp +++ b/ViroRenderer/VROPlatformUtil.cpp @@ -553,6 +553,15 @@ static jobject sJavaAssetMgr = nullptr; static jobject sPlatformUtil = nullptr; static AAssetManager *sAssetMgr = nullptr; +// The JavaVM is known from the moment System.loadLibrary("viro_renderer") loads this library, so +// a JNI call that reaches getJNIEnv() before any renderer or loader has called VROPlatformSetEnv +// still finds it. Under Fabric a view such as VRTCamera is built before the scene's renderer, and +// adding its node already calls back into Java. +extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) { + sVM = vm; + return JNI_VERSION_1_6; +} + // Map (and mutex) holding native tasks waiting to be dispatched. On Android the threading // functionality is handled on the Java layer, so we need some mechanism for mapping IDs // to corresponding tasks. Both the ID generator and the map itself are protected by the