blob: cab4b8de202e77d70282b594a1befbd75b06cb2d [file] [log] [blame]
/*
* 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 <pal/spi/cocoa/SceneKitSPI.h>
namespace WebCore {
class SceneKitModelLoaderUSD final : public SceneKitModelLoader {
public:
static Ref<SceneKitModelLoaderUSD> 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<SceneKitModelUSD> create(Ref<Model> modelSource, RetainPtr<SCNScene> scene)
{
return adoptRef(*new SceneKitModelUSD(WTFMove(modelSource), WTFMove(scene)));
}
virtual ~SceneKitModelUSD() = default;
private:
SceneKitModelUSD(Ref<Model> modelSource, RetainPtr<SCNScene> 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<SCNScene *> *scenes() const override
{
return @[ m_scene.get() ];
}
Ref<Model> m_modelSource;
RetainPtr<SCNScene> m_scene;
};
static RetainPtr<NSURL> 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<SceneKitModelLoader> 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