/* * Copyright (C) 2021 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #import "config.h" #if HAVE(SCENEKIT) #import "SceneKitModelLoaderUSD.h" #import "Model.h" #import "ResourceError.h" #import "SceneKitModel.h" #import "SceneKitModelLoader.h" #import "SceneKitModelLoaderClient.h" #import namespace WebCore { class SceneKitModelLoaderUSD final : public SceneKitModelLoader { public: static Ref create() { return adoptRef(*new SceneKitModelLoaderUSD()); } virtual ~SceneKitModelLoaderUSD() = default; virtual void cancel() final { m_canceled = true; } bool isCanceled() const { return m_canceled; } private: SceneKitModelLoaderUSD() : m_canceled { false } { } bool m_canceled; }; class SceneKitModelUSD final : public SceneKitModel { public: static Ref create(Ref modelSource, RetainPtr scene) { return adoptRef(*new SceneKitModelUSD(WTFMove(modelSource), WTFMove(scene))); } virtual ~SceneKitModelUSD() = default; private: SceneKitModelUSD(Ref modelSource, RetainPtr scene) : m_modelSource { WTFMove(modelSource) } , m_scene { WTFMove(scene) } { } // SceneKitModel overrides. virtual const Model& modelSource() const override { return m_modelSource.get(); } virtual SCNScene *defaultScene() const override { return m_scene.get(); } virtual NSArray *scenes() const override { return @[ m_scene.get() ]; } Ref m_modelSource; RetainPtr m_scene; }; static RetainPtr writeToTemporaryFile(WebCore::Model& modelSource) { // FIXME: DO NOT SHIP!!! We must not write these to disk; we need SceneKit // to support reading USD files from its [SCNSceneSource initWithData:options:], // initializer but currently that does not work. FileSystem::PlatformFileHandle fileHandle; auto filePath = FileSystem::openTemporaryFile("ModelFile"_s, fileHandle, ".usdz"_s); ASSERT(FileSystem::isHandleValid(fileHandle)); size_t byteCount = FileSystem::writeToFile(fileHandle, modelSource.data()->makeContiguous()->data(), modelSource.data()->size()); ASSERT_UNUSED(byteCount, byteCount == modelSource.data()->size()); FileSystem::closeFile(fileHandle); return adoptNS([[NSURL alloc] initFileURLWithPath:filePath]); } Ref loadSceneKitModelUsingUSDLoader(Model& modelSource, SceneKitModelLoaderClient& client) { auto loader = SceneKitModelLoaderUSD::create(); dispatch_async(dispatch_get_main_queue(), [weakClient = WeakPtr { client }, loader, modelSource = Ref { modelSource }] () mutable { // If the client has gone away, there is no reason to do any work. auto strongClient = weakClient.get(); if (!strongClient) return; // If the caller has canceled the load, there is no reason to do any work. if (loader->isCanceled()) return; auto url = writeToTemporaryFile(modelSource.get()); auto source = adoptNS([[SCNSceneSource alloc] initWithURL:url.get() options:nil]); NSError *error = nil; RetainPtr scene = [source sceneWithOptions:@{ } error:&error]; if (error) { strongClient->didFailLoading(loader.get(), ResourceError(error)); [error release]; return; } ASSERT(scene); strongClient->didFinishLoading(loader.get(), SceneKitModelUSD::create(WTFMove(modelSource), WTFMove(scene))); }); return loader; } } #endif