Compare commits

...

44 Commits

Author SHA1 Message Date
D8H
3c34866faa Fix the dragging of the first 3D animation (#5414)
* Don't show in changelog
2023-06-15 16:42:51 +02:00
AlexandreS
69cd2784c4 Add alert message when changing the 3D rendering in scene editor (#5412)
Do not show in changelog
2023-06-15 09:27:37 +02:00
AlexandreS
5b7e419a41 Improve user experience with users autocomplete (#5410) 2023-06-14 19:03:37 +02:00
AlexandreS
7773460d35 Autodetect webgl support to define 3D instances showing preference (#5409)
Don't show in changelog
2023-06-14 15:30:27 +02:00
AlexandreS
9262266480 Fix wrongly set flag (#5406)
Do not show in changelog
2023-06-14 14:59:42 +02:00
AlexandreS
84f2b4ca68 Bump newIDE version (#5407) 2023-06-14 14:58:38 +02:00
AlexandreS
19ae7a378c Prevent empty Algolia search at app start (#5405)
only show in developer changelog
2023-06-14 14:57:47 +02:00
github-actions[bot]
f9ca330add Update translations [skip ci] (#5403) 2023-06-14 14:10:30 +02:00
D8H
5ef990ac7d Fix 3D model positions in the 2D editor (#5404)
* Don't show in changelog
2023-06-14 11:34:26 +02:00
D8H
8099820729 Fix double dot in descriptions (#5402)
* Don't show in changelog.
2023-06-14 10:20:25 +02:00
github-actions[bot]
df556f20e9 Update translations [skip ci] (#5399)
Co-authored-by: ClementPasteau <ClementPasteau@users.noreply.github.com>
2023-06-14 09:37:57 +02:00
Clément Pasteau
2c8f2ab58d New field to create a project from a prompt (#5395)
* New Experimental field in the Create Project Dialog allowing to enter a game description and get a game generated by the AI
2023-06-13 17:15:06 +02:00
D8H
20c3d62c90 Add the support for animations on 3D models (#5302)
- Breaking change: fix 3D models that were mirrored on Y axis.
  - In case some models look upside-down, they can be fixed by adding 180° to the "Rotation around Y axis" property.
- Handle custom origin and center.
2023-06-13 12:42:46 +02:00
github-actions[bot]
0a28981c74 Update translations [skip ci] (#5388)
Co-authored-by: D8H <D8H@users.noreply.github.com>
2023-06-13 10:19:16 +02:00
D8H
c8bb24475c Use the project name when creating a new project file locally (#5396) 2023-06-12 15:17:41 +02:00
AlexandreS
81bce61783 Introduce Education plan (#5382)
It is now possible to subscribe to an education plan to provide gold subscriptions to a pool of anonymised users.
2023-06-12 10:32:21 +02:00
Florian Rival
71d1b6aa1f Refactor handling of prices of asset packs (#5393)
Don't show in changelog
2023-06-11 20:33:42 +02:00
Clément Pasteau
c41974c24b Update fling game (#5391)
Do not show in changelog
2023-06-09 17:52:08 +02:00
D8H
3bee88c6cd Handle 3D models compressed with Draco (#5390) 2023-06-09 17:49:46 +02:00
D8H
4c874dfb7e Automatically set a default operator when adding a new condition or action (#5389) 2023-06-09 14:59:04 +02:00
D8H
65f499f24e Fix the alert message about 3d objects without any light (#5387)
* Don't show in changelog
2023-06-09 10:30:20 +02:00
github-actions[bot]
3265bf7fb4 Update translations [skip ci] (#5386)
Co-authored-by: AlexandreSi <AlexandreSi@users.noreply.github.com>
2023-06-09 10:27:25 +02:00
AlexandreS
5a437dea4e Fix markdown tables rendering (for extension description for instance) (#5384) 2023-06-09 10:00:06 +02:00
github-actions[bot]
19b576e8cc Update translations [skip ci] (#5383)
Co-authored-by: ClementPasteau <ClementPasteau@users.noreply.github.com>
2023-06-08 16:07:35 +02:00
Clément Pasteau
67747e458e Add new guided lesson: Create a 3D Box (#5376) 2023-06-08 11:37:29 +02:00
github-actions[bot]
260c2b9c8f Update translations [skip ci] (#5380)
Co-authored-by: D8H <D8H@users.noreply.github.com>
2023-06-08 10:30:14 +02:00
D8H
255ef1d8ef Add an antialising setting for 3D (#5381) 2023-06-08 09:57:34 +02:00
D8H
53c633c646 Add an hemisphere light effect for 3D layers (#5379) 2023-06-07 17:21:26 +02:00
github-actions[bot]
cec67a91d4 Update translations [skip ci] (#5370)
Co-authored-by: ClementPasteau <ClementPasteau@users.noreply.github.com>
2023-06-07 16:59:32 +02:00
Clément Pasteau
4408dfe59d Fix initializing received assets correctly when user is not authenticated (#5374)
* Asset pack web links were not working because of this
2023-06-07 16:59:08 +02:00
Clément Pasteau
c4274d2fc4 Fix default depth of 3D object on scene being correctly initialised (#5378) 2023-06-07 15:13:04 +02:00
Clément Pasteau
b0103f31b7 Fix showing cancel subscription button for legacy plans (#5371) 2023-06-07 10:23:39 +02:00
AlexandreS
18905890d4 Fix dialogue tree loading from scene variable action sentence (#5372) 2023-06-06 19:01:05 +02:00
D8H
6858e0fb59 Render 3D in linear color space, upgrade to Three.js 1.152.0 (#5360)
* 3D model objects will look brighter. Light intensity may need to be reduced.
2023-06-06 12:28:46 +02:00
github-actions[bot]
cf595a7d7d Update translations [skip ci] (#5364)
Co-authored-by: 4ian <4ian@users.noreply.github.com>
2023-06-05 13:42:31 +02:00
Florian Rival
e681d27bb8 Allow to use the 3D editor with a toggle in the preferences 2023-06-03 14:19:00 +02:00
AlexandreS
8941e04390 Fix color 3D model color rendering being darker than the original texture (#5368) 2023-06-02 15:46:27 +02:00
AlexandreS
e186681f39 Display Z coordinate in instance tooltip for instances of 3D objects
- Also fixes some weird behavior around the "Custom size" checkbox in instance properties
2023-06-01 16:12:34 +02:00
Florian Rival
a578fa32e9 Add support for 3D objects in editor (#5357) 2023-06-01 12:17:20 +02:00
supertree-wook
6b40e8309c Fix some extension descriptions in the wiki not having properly formatted lists (#5365) 2023-06-01 11:56:14 +02:00
supertree-wook
5ff51351af Fix typo in description of StrFindLastFrom (#5363) 2023-05-31 09:57:13 +02:00
github-actions[bot]
d66e4e0001 Update translations [skip ci] (#5351)
Co-authored-by: ClementPasteau <ClementPasteau@users.noreply.github.com>
2023-05-30 18:02:33 +02:00
Clément Pasteau
2184eaf70b Fix sprite images selection when opening up the options menu (#5359)
* Fix an issue where opening the options menu of a sprite of a non-selected image would not take it into account for the actions.
2023-05-30 11:18:23 +02:00
AlexandreS
29fc0598f6 Start over from the suffix number when generating a new name (#5355) 2023-05-26 11:36:51 +02:00
225 changed files with 10776 additions and 2618 deletions

View File

@@ -287,7 +287,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
obj.AddCondition("AnimationName",
_("Current animation name"),
_("Check the animation by played by the object."),
_("Check the animation played by the object."),
_("The animation of _PARAM0_ is _PARAM1_"),
_("Animations and images"),
"res/conditions/animation24.png",

View File

@@ -172,7 +172,7 @@ BuiltinExtensionsImplementer::ImplementsStringInstructionsExtension(
_("Search the last occurrence in a text, starting from a position"),
_("Search in a text the last occurrence, starting from a position "
"(return "
" the position of the result, from the beginning of the string, or "
"the position of the result, from the beginning of the string, or "
"-1 if not found)"),
"",
"res/conditions/toujours24_black.png")

View File

@@ -9,9 +9,9 @@
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Object.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/PropertyDescriptor.h"
#include "GDCore/Serialization/SerializerElement.h"
#include "GDCore/Tools/UUID/UUID.h"
#include "GDCore/Project/PropertyDescriptor.h"
namespace gd {
@@ -21,12 +21,17 @@ InitialInstance::InitialInstance()
: objectName(""),
x(0),
y(0),
z(0),
angle(0),
rotationX(0),
rotationY(0),
zOrder(0),
layer(""),
personalizedSize(false),
customSize(false),
customDepth(false),
width(0),
height(0),
depth(0),
locked(false),
sealed(false),
persistentUuid(UUID::MakeUuid4()) {}
@@ -35,11 +40,20 @@ void InitialInstance::UnserializeFrom(const SerializerElement& element) {
SetObjectName(element.GetStringAttribute("name", "", "nom"));
SetX(element.GetDoubleAttribute("x"));
SetY(element.GetDoubleAttribute("y"));
SetZ(element.GetDoubleAttribute("z", 0));
SetAngle(element.GetDoubleAttribute("angle"));
SetRotationX(element.GetDoubleAttribute("rotationX", 0));
SetRotationY(element.GetDoubleAttribute("rotationY", 0));
SetHasCustomSize(
element.GetBoolAttribute("customSize", false, "personalizedSize"));
SetCustomWidth(element.GetDoubleAttribute("width"));
SetCustomHeight(element.GetDoubleAttribute("height"));
if (element.HasChild("depth") || element.HasAttribute("depth")) {
SetHasCustomDepth(true);
SetCustomDepth(element.GetDoubleAttribute("depth"));
} else {
SetHasCustomDepth(false);
}
SetZOrder(element.GetIntAttribute("zOrder", 0, "plan"));
SetLayer(element.GetStringAttribute("layer"));
SetLocked(element.GetBoolAttribute("locked", false));
@@ -53,9 +67,26 @@ void InitialInstance::UnserializeFrom(const SerializerElement& element) {
element.GetChild("numberProperties", 0, "floatInfos");
numberPropertiesElement.ConsiderAsArrayOf("property", "Info");
for (std::size_t j = 0; j < numberPropertiesElement.GetChildrenCount(); ++j) {
gd::String name = numberPropertiesElement.GetChild(j).GetStringAttribute("name");
double value = numberPropertiesElement.GetChild(j).GetDoubleAttribute("value");
numberProperties[name] = value;
gd::String name =
numberPropertiesElement.GetChild(j).GetStringAttribute("name");
double value =
numberPropertiesElement.GetChild(j).GetDoubleAttribute("value");
// Compatibility with GD <= 5.1.164
if (name == "z") {
SetZ(value);
} else if (name == "rotationX") {
SetRotationX(value);
} else if (name == "rotationY") {
SetRotationY(value);
} else if (name == "depth") {
SetHasCustomDepth(true);
SetCustomDepth(value);
}
// end of compatibility code
else {
numberProperties[name] = value;
}
}
stringProperties.clear();
@@ -77,21 +108,26 @@ void InitialInstance::SerializeTo(SerializerElement& element) const {
element.SetAttribute("name", GetObjectName());
element.SetAttribute("x", GetX());
element.SetAttribute("y", GetY());
if (GetZ() != 0) element.SetAttribute("z", GetZ());
element.SetAttribute("zOrder", GetZOrder());
element.SetAttribute("layer", GetLayer());
element.SetAttribute("angle", GetAngle());
if (GetRotationX() != 0) element.SetAttribute("rotationX", GetRotationX());
if (GetRotationY() != 0) element.SetAttribute("rotationY", GetRotationY());
element.SetAttribute("customSize", HasCustomSize());
element.SetAttribute("width", GetCustomWidth());
element.SetAttribute("height", GetCustomHeight());
if (HasCustomDepth()) element.SetAttribute("depth", GetCustomDepth());
if (IsLocked()) element.SetAttribute("locked", IsLocked());
if (IsSealed()) element.SetAttribute("sealed", IsSealed());
if (persistentUuid.empty()) persistentUuid = UUID::MakeUuid4();
element.SetStringAttribute("persistentUuid", persistentUuid);
SerializerElement& numberPropertiesElement = element.AddChild("numberProperties");
SerializerElement& numberPropertiesElement =
element.AddChild("numberProperties");
numberPropertiesElement.ConsiderAsArrayOf("property");
for (const auto& property: numberProperties) {
for (const auto& property : numberProperties) {
numberPropertiesElement.AddChild("property")
.SetAttribute("name", property.first)
.SetAttribute("value", property.second);
@@ -99,7 +135,7 @@ void InitialInstance::SerializeTo(SerializerElement& element) const {
SerializerElement& stringPropElement = element.AddChild("stringProperties");
stringPropElement.ConsiderAsArrayOf("property");
for (const auto& property: stringProperties) {
for (const auto& property : stringProperties) {
stringPropElement.AddChild("property")
.SetAttribute("name", property.first)
.SetAttribute("value", property.second);
@@ -117,10 +153,12 @@ std::map<gd::String, gd::PropertyDescriptor>
InitialInstance::GetCustomProperties(gd::Project& project, gd::Layout& layout) {
// Find an object
if (layout.HasObjectNamed(GetObjectName()))
return layout.GetObject(GetObjectName()).GetConfiguration()
return layout.GetObject(GetObjectName())
.GetConfiguration()
.GetInitialInstanceProperties(*this, project, layout);
else if (project.HasObjectNamed(GetObjectName()))
return project.GetObject(GetObjectName()).GetConfiguration()
return project.GetObject(GetObjectName())
.GetConfiguration()
.GetInitialInstanceProperties(*this, project, layout);
std::map<gd::String, gd::PropertyDescriptor> nothing;
@@ -132,10 +170,12 @@ bool InitialInstance::UpdateCustomProperty(const gd::String& name,
gd::Project& project,
gd::Layout& layout) {
if (layout.HasObjectNamed(GetObjectName()))
return layout.GetObject(GetObjectName()).GetConfiguration()
return layout.GetObject(GetObjectName())
.GetConfiguration()
.UpdateInitialInstanceProperty(*this, name, value, project, layout);
else if (project.HasObjectNamed(GetObjectName()))
return project.GetObject(GetObjectName()).GetConfiguration()
return project.GetObject(GetObjectName())
.GetConfiguration()
.UpdateInitialInstanceProperty(*this, name, value, project, layout);
return false;
@@ -154,7 +194,8 @@ const gd::String& InitialInstance::GetRawStringProperty(
return it != stringProperties.end() ? it->second : *badStringProperyValue;
}
void InitialInstance::SetRawDoubleProperty(const gd::String& name, double value) {
void InitialInstance::SetRawDoubleProperty(const gd::String& name,
double value) {
numberProperties[name] = value;
}

View File

@@ -73,22 +73,52 @@ class GD_CORE_API InitialInstance {
void SetY(double y_) { y = y_; }
/**
* \brief Get the rotation of the instance, in radians.
* \brief Get the Z position of the instance
*/
double GetZ() const { return z; }
/**
* \brief Set the Z position of the instance
*/
void SetZ(double z_) { z = z_; }
/**
* \brief Get the rotation of the instance on Z axis, in radians.
*/
double GetAngle() const { return angle; }
/**
* \brief Set the rotation of the instance, in radians.
* \brief Set the rotation of the instance on Z axis, in radians.
*/
void SetAngle(double angle_) { angle = angle_; }
/**
* \brief Get the Z order of the instance.
* \brief Get the rotation of the instance on X axis, in radians.
*/
double GetRotationX() const { return rotationX; }
/**
* \brief Set the rotation of the instance on X axis, in radians.
*/
void SetRotationX(double rotationX_) { rotationX = rotationX_; }
/**
* \brief Get the rotation of the instance on Y axis, in radians.
*/
double GetRotationY() const { return rotationY; }
/**
* \brief Set the rotation of the instance on Y axis, in radians.
*/
void SetRotationY(double rotationY_) { rotationY = rotationY_; }
/**
* \brief Get the Z order of the instance (for a 2D object).
*/
int GetZOrder() const { return zOrder; }
/**
* \brief Set the Z order of the instance.
* \brief Set the Z order of the instance (for a 2D object).
*/
void SetZOrder(int zOrder_) { zOrder = zOrder_; }
@@ -103,29 +133,51 @@ class GD_CORE_API InitialInstance {
void SetLayer(const gd::String& layer_) { layer = layer_; }
/**
* \brief Return true if the instance has a size which is different from its
* object default size.
* \brief Return true if the instance has a width/height which is different from its
* object default width/height. This is independent from `HasCustomDepth`.
*
* \see gd::Object
*/
bool HasCustomSize() const { return personalizedSize; }
bool HasCustomSize() const { return customSize; }
/**
* \brief Set whether the instance has a size which is different from its
* object default size or not.
* \brief Return true if the instance has a depth which is different from its
* object default depth. This is independent from `HasCustomSize`.
*
* \param hasCustomSize true if the size is different from the object's
* default size. \see gd::Object
* \see gd::Object
*/
bool HasCustomDepth() const { return customDepth; }
/**
* \brief Set whether the instance has a width/height which is different from its
* object default width/height or not.
* This is independent from `SetHasCustomDepth`.
*
* \see gd::Object
*/
void SetHasCustomSize(bool hasCustomSize_) {
personalizedSize = hasCustomSize_;
customSize = hasCustomSize_;
}
/**
* \brief Set whether the instance has a depth which is different from its
* object default depth or not.
* This is independent from `SetHasCustomSize`.
*
* \param hasCustomSize true if the depth is different from the object's
* default depth.
* \see gd::Object
*/
void SetHasCustomDepth(bool hasCustomDepth_) {
customDepth = hasCustomDepth_;
}
double GetCustomWidth() const { return width; }
void SetCustomWidth(double width_) { width = width_; }
double GetCustomHeight() const { return height; }
void SetCustomHeight(double height_) { height = height_; }
double GetCustomDepth() const { return depth; }
void SetCustomDepth(double depth_) { depth = depth_; }
/**
* \brief Return true if the instance is locked and cannot be moved in the
@@ -272,14 +324,19 @@ class GD_CORE_API InitialInstance {
stringProperties; ///< More data which can be used by the object
gd::String objectName; ///< Object name
double x; ///< Object initial X position
double y; ///< Object initial Y position
double angle; ///< Object initial angle
int zOrder; ///< Object initial Z order
gd::String layer; ///< Object initial layer
bool personalizedSize; ///< True if object has a custom size
double width; ///< Object custom width
double height; ///< Object custom height
double x; ///< Instance X position
double y; ///< Instance Y position
double z; ///< Instance Z position (for a 3D object)
double angle; ///< Instance angle on Z axis
double rotationX; ///< Instance angle on X axis (for a 3D object)
double rotationY; ///< Instance angle on Y axis (for a 3D object)
int zOrder; ///< Instance Z order (for a 2D object)
gd::String layer; ///< Instance layer
bool customSize; ///< True if object has a custom width and height
bool customDepth; ///< True if object has a custom depth
double width; ///< Instance custom width
double height; ///< Instance custom height
double depth; ///< Instance custom depth
gd::VariablesContainer initialVariables; ///< Instance specific variables
bool locked; ///< True if the instance is locked
bool sealed; ///< True if the instance is sealed

View File

@@ -127,6 +127,10 @@ class GD_CORE_API Object {
/** \brief Return the tags of the object.
*/
const gd::String& GetTags() const { return tags; }
/** \brief Shortcut to check if the object is a 3D object.
*/
bool Is3DObject() const { return configuration->Is3DObject(); }
///@}
/** \name Behaviors management

View File

@@ -20,7 +20,7 @@ namespace gd {
ObjectConfiguration::~ObjectConfiguration() {}
ObjectConfiguration::ObjectConfiguration() {}
ObjectConfiguration::ObjectConfiguration(): is3DObject(false) {}
std::map<gd::String, gd::PropertyDescriptor> ObjectConfiguration::GetProperties() const {
std::map<gd::String, gd::PropertyDescriptor> nothing;

View File

@@ -61,12 +61,22 @@ class GD_CORE_API ObjectConfiguration {
/** \brief Change the type of the object.
*/
void SetType(const gd::String& type_) { type = type_; }
void SetType(const gd::String& type_) {
type = type_;
// For now, as a shortcut, consider only the objects from the built-in 3D extension
// to be 3D object.
is3DObject = type.find("Scene3D::") == 0;
}
/** \brief Return the type of the object.
*/
const gd::String& GetType() const { return type; }
/** \brief Shortcut to check if the object is a 3D object.
*/
bool Is3DObject() const { return is3DObject; }
/** \name Object properties
* Reading and updating object configuration properties
*/
@@ -170,6 +180,7 @@ class GD_CORE_API ObjectConfiguration {
protected:
gd::String type; ///< Which type of object is represented by this
///< configuration.
bool is3DObject;
/**
* \brief Derived object configuration can redefine this method to load

View File

@@ -65,6 +65,8 @@ Project::Project()
pixelsRounding(false),
adaptGameResolutionAtRuntime(true),
sizeOnStartupMode("adaptWidth"),
antialiasingMode("MSAA"),
isAntialisingEnabledOnMobile(false),
projectUuid(""),
useDeprecatedZeroAsDefaultZOrder(false),
useExternalSourceFiles(false),
@@ -628,6 +630,8 @@ void Project::UnserializeFrom(const SerializerElement& element) {
SetAdaptGameResolutionAtRuntime(
propElement.GetBoolAttribute("adaptGameResolutionAtRuntime", false));
SetSizeOnStartupMode(propElement.GetStringAttribute("sizeOnStartupMode", ""));
SetAntialiasingMode(propElement.GetStringAttribute("antialiasingMode", "MSAA"));
SetAntialisingEnabledOnMobile(propElement.GetBoolAttribute("antialisingEnabledOnMobile", false));
SetProjectUuid(propElement.GetStringAttribute("projectUuid", ""));
SetAuthor(propElement.GetChild("author", 0, "Auteur").GetValue().GetString());
SetPackageName(propElement.GetStringAttribute("packageName"));
@@ -882,6 +886,8 @@ void Project::SerializeTo(SerializerElement& element) const {
propElement.SetAttribute("adaptGameResolutionAtRuntime",
adaptGameResolutionAtRuntime);
propElement.SetAttribute("sizeOnStartupMode", sizeOnStartupMode);
propElement.SetAttribute("antialiasingMode", antialiasingMode);
propElement.SetAttribute("antialisingEnabledOnMobile", isAntialisingEnabledOnMobile);
propElement.SetAttribute("projectUuid", projectUuid);
propElement.SetAttribute("folderProject", folderProject);
propElement.SetAttribute("packageName", packageName);
@@ -1113,6 +1119,8 @@ void Project::Init(const gd::Project& game) {
pixelsRounding = game.pixelsRounding;
adaptGameResolutionAtRuntime = game.adaptGameResolutionAtRuntime;
sizeOnStartupMode = game.sizeOnStartupMode;
antialiasingMode = game.antialiasingMode;
isAntialisingEnabledOnMobile = game.isAntialisingEnabledOnMobile;
projectUuid = game.projectUuid;
useDeprecatedZeroAsDefaultZOrder = game.useDeprecatedZeroAsDefaultZOrder;

View File

@@ -383,6 +383,26 @@ class GD_CORE_API Project : public ObjectsContainer {
*/
void SetPixelsRounding(bool enable) { pixelsRounding = enable; }
/**
* Return the antialiasing mode used by the game ("none" or "MSAA").
*/
const gd::String& GetAntialiasingMode() const { return antialiasingMode; }
/**
* Set the antialiasing mode used by the game ("none" or "MSAA").
*/
void SetAntialiasingMode(const gd::String& antialiasingMode_) { antialiasingMode = antialiasingMode_; }
/**
* Return true if antialising is enabled on mobiles.
*/
bool IsAntialisingEnabledOnMobile() const { return isAntialisingEnabledOnMobile; }
/**
* Set whether antialising is enabled on mobiles or not.
*/
void SetAntialisingEnabledOnMobile(bool enable) { isAntialisingEnabledOnMobile = enable; }
/**
* \brief Return if the project should set 0 as Z-order for objects created
* from events (which is deprecated) - instead of the highest Z order that was
@@ -1040,6 +1060,8 @@ class GD_CORE_API Project : public ObjectsContainer {
gd::String
sizeOnStartupMode; ///< How to adapt the game size to the screen. Can be
///< "adaptWidth", "adaptHeight" or empty
gd::String antialiasingMode;
bool isAntialisingEnabledOnMobile;
gd::String projectUuid; ///< UUID useful to identify the game in online
///< services or database that would require it.
bool useDeprecatedZeroAsDefaultZOrder; ///< If true, objects created from

View File

@@ -108,19 +108,8 @@ namespace gdjs {
this.setWidth(initialInstanceData.width);
this.setHeight(initialInstanceData.height);
}
initialInstanceData.numberProperties.forEach((property) => {
if (property.name === 'z') {
this.setZ(property.value);
} else if (property.name === 'depth') {
if (initialInstanceData.customSize) {
this.setDepth(property.value);
}
} else if (property.name === 'rotationX') {
this.setRotationX(property.value);
} else if (property.name === 'rotationY') {
this.setRotationY(property.value);
}
});
if (initialInstanceData.depth !== undefined)
this.setDepth(initialInstanceData.depth);
}
setX(x: float): void {

View File

@@ -0,0 +1,20 @@
cmake_minimum_required(VERSION 2.6)
cmake_policy(SET CMP0015 NEW)
project(Scene3D)
gd_add_extension_includes()
#Defines
###
gd_add_extension_definitions(Scene3D)
#The targets
###
include_directories(.)
file(GLOB source_files *.cpp *.h)
gd_add_clang_utils(Scene3D "${source_files}")
gd_add_extension_target(Scene3D "${source_files}")
#Linker files for the IDE extension
###
gd_extension_link_libraries(Scene3D)

View File

@@ -0,0 +1,106 @@
namespace gdjs {
gdjs.PixiFiltersTools.registerFilterCreator(
'Scene3D::HemisphereLight',
new (class implements gdjs.PixiFiltersTools.FilterCreator {
makeFilter(
target: EffectsTarget,
effectData: EffectData
): gdjs.PixiFiltersTools.Filter {
return new (class implements gdjs.PixiFiltersTools.Filter {
light: THREE.HemisphereLight;
rotationObject: THREE.Group;
_isEnabled: boolean = false;
top: string = 'Y-';
elevation: float = 45;
rotation: float = 0;
constructor() {
this.light = new THREE.HemisphereLight();
this.light.position.set(1, 0, 0);
this.rotationObject = new THREE.Group();
this.rotationObject.add(this.light);
this.updateRotation();
}
isEnabled(target: EffectsTarget): boolean {
return this._isEnabled;
}
setEnabled(target: EffectsTarget, enabled: boolean): boolean {
if (this._isEnabled === enabled) {
return true;
}
if (enabled) {
return this.applyEffect(target);
} else {
return this.removeEffect(target);
}
}
applyEffect(target: EffectsTarget): boolean {
const scene = target.get3DRendererObject() as
| THREE.Scene
| null
| undefined;
if (!scene) {
return false;
}
scene.add(this.rotationObject);
this._isEnabled = true;
return true;
}
removeEffect(target: EffectsTarget): boolean {
const scene = target.get3DRendererObject() as
| THREE.Scene
| null
| undefined;
if (!scene) {
return false;
}
scene.remove(this.rotationObject);
this._isEnabled = false;
return true;
}
updatePreRender(target: gdjs.EffectsTarget): any {}
updateDoubleParameter(parameterName: string, value: number): void {
if (parameterName === 'intensity') {
this.light.intensity = value;
} else if (parameterName === 'elevation') {
this.elevation = value;
this.updateRotation();
} else if (parameterName === 'rotation') {
this.rotation = value;
this.updateRotation();
}
}
updateStringParameter(parameterName: string, value: string): void {
if (parameterName === 'skyColor') {
this.light.color = new THREE.Color(
gdjs.PixiFiltersTools.rgbOrHexToHexNumber(value)
);
}
if (parameterName === 'groundColor') {
this.light.groundColor = new THREE.Color(
gdjs.PixiFiltersTools.rgbOrHexToHexNumber(value)
);
}
if (parameterName === 'top') {
this.top = value;
this.updateRotation();
}
}
updateBooleanParameter(parameterName: string, value: boolean): void {}
updateRotation() {
if (this.top === 'Z+') {
// 0° is a light from the right of the screen.
this.rotationObject.rotation.z = gdjs.toRad(this.rotation);
this.rotationObject.rotation.y = -gdjs.toRad(this.elevation);
} else {
// 0° becomes a light from Z+.
this.rotationObject.rotation.y = gdjs.toRad(this.rotation) - 90;
this.rotationObject.rotation.z = -gdjs.toRad(this.elevation);
}
}
})();
}
})()
);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,292 @@
/**
GDevelop - Particle System Extension
Copyright (c) 2010-2016 Florian Rival (Florian.Rival@gmail.com)
This project is released under the MIT License.
*/
#include "Model3DObjectConfiguration.h"
#include "GDCore/CommonTools.h"
#include "GDCore/IDE/Project/ArbitraryResourceWorker.h"
#include "GDCore/Project/InitialInstance.h"
#include "GDCore/Project/MeasurementUnit.h"
#include "GDCore/Project/Object.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/PropertyDescriptor.h"
#include "GDCore/Serialization/SerializerElement.h"
#include "GDCore/Tools/Localization.h"
using namespace std;
Model3DObjectConfiguration::Model3DObjectConfiguration()
: width(100), height(100), depth(100), rotationX(0), rotationY(0),
rotationZ(0), modelResourceName(""), materialType("Basic"),
originLocation("ModelOrigin"), centerLocation("ModelOrigin"),
keepAspectRatio(true) {}
bool Model3DObjectConfiguration::UpdateProperty(const gd::String &propertyName,
const gd::String &newValue) {
if (propertyName == "width") {
width = newValue.To<double>();
return true;
}
if (propertyName == "height") {
height = newValue.To<double>();
return true;
}
if (propertyName == "depth") {
depth = newValue.To<double>();
return true;
}
if (propertyName == "rotationX") {
rotationX = newValue.To<double>();
return true;
}
if (propertyName == "rotationY") {
rotationY = newValue.To<double>();
return true;
}
if (propertyName == "rotationZ") {
rotationZ = newValue.To<double>();
return true;
}
if (propertyName == "modelResourceName") {
modelResourceName = newValue;
return true;
}
if (propertyName == "materialType") {
materialType = newValue;
return true;
}
if (propertyName == "originLocation") {
originLocation = newValue;
return true;
}
if (propertyName == "centerLocation") {
centerLocation = newValue;
return true;
}
if (propertyName == "keepAspectRatio") {
keepAspectRatio = newValue == "1";
return true;
}
return false;
}
std::map<gd::String, gd::PropertyDescriptor>
Model3DObjectConfiguration::GetProperties() const {
std::map<gd::String, gd::PropertyDescriptor> objectProperties;
objectProperties["width"]
.SetValue(gd::String::From(width))
.SetType("number")
.SetLabel(_("Width"))
.SetMeasurementUnit(gd::MeasurementUnit::GetPixel())
.SetGroup(_("Default size"));
objectProperties["height"]
.SetValue(gd::String::From(height))
.SetType("number")
.SetLabel(_("Height"))
.SetMeasurementUnit(gd::MeasurementUnit::GetPixel())
.SetGroup(_("Default size"));
objectProperties["depth"]
.SetValue(gd::String::From(depth))
.SetType("number")
.SetLabel(_("Depth"))
.SetMeasurementUnit(gd::MeasurementUnit::GetPixel())
.SetGroup(_("Default size"));
objectProperties["keepAspectRatio"]
.SetValue(keepAspectRatio ? "true" : "false")
.SetType("boolean")
.SetLabel(_("Reduce initial dimensions to keep aspect ratio"))
.SetGroup(_("Default size"));
objectProperties["rotationX"]
.SetValue(gd::String::From(rotationX))
.SetType("number")
.SetLabel(_("Rotation around X axis"))
.SetMeasurementUnit(gd::MeasurementUnit::GetDegreeAngle())
.SetGroup(_("Default orientation"));
objectProperties["rotationY"]
.SetValue(gd::String::From(rotationY))
.SetType("number")
.SetLabel(_("Rotation around Y axis"))
.SetMeasurementUnit(gd::MeasurementUnit::GetDegreeAngle())
.SetGroup(_("Default orientation"));
objectProperties["rotationZ"]
.SetValue(gd::String::From(rotationZ))
.SetType("number")
.SetLabel(_("Rotation around Z axis"))
.SetMeasurementUnit(gd::MeasurementUnit::GetDegreeAngle())
.SetGroup(_("Default orientation"));
objectProperties["modelResourceName"]
.SetValue(modelResourceName)
.SetType("resource")
.AddExtraInfo("model3D")
.SetLabel(_("3D model"));
objectProperties["materialType"]
.SetValue(materialType.empty() ? "Basic" : materialType)
.SetType("choice")
.AddExtraInfo("Basic")
.AddExtraInfo("StandardWithoutMetalness")
.AddExtraInfo("KeepOriginal")
.SetLabel(_("Material modifier"));
objectProperties["originLocation"]
.SetValue(originLocation.empty() ? "TopLeft" : originLocation)
.SetType("choice")
.AddExtraInfo("ModelOrigin")
.AddExtraInfo("TopLeft")
.AddExtraInfo("ObjectCenter")
.AddExtraInfo("BottomCenterZ")
.AddExtraInfo("BottomCenterY")
.SetLabel(_("Origin point"));
objectProperties["centerLocation"]
.SetValue(centerLocation.empty() ? "ObjectCenter" : centerLocation)
.SetType("choice")
.AddExtraInfo("ModelOrigin")
.AddExtraInfo("ObjectCenter")
.AddExtraInfo("BottomCenterZ")
.AddExtraInfo("BottomCenterY")
.SetLabel(_("Center point"));
return objectProperties;
}
bool Model3DObjectConfiguration::UpdateInitialInstanceProperty(
gd::InitialInstance &instance, const gd::String &propertyName,
const gd::String &newValue, gd::Project &project, gd::Layout &layout) {
return false;
}
std::map<gd::String, gd::PropertyDescriptor>
Model3DObjectConfiguration::GetInitialInstanceProperties(
const gd::InitialInstance &instance, gd::Project &project,
gd::Layout &layout) {
std::map<gd::String, gd::PropertyDescriptor> instanceProperties;
return instanceProperties;
}
void Model3DObjectConfiguration::DoUnserializeFrom(
gd::Project &project, const gd::SerializerElement &element) {
auto &content = element.GetChild("content");
width = content.GetDoubleAttribute("width");
height = content.GetDoubleAttribute("height");
depth = content.GetDoubleAttribute("depth");
rotationX = content.GetDoubleAttribute("rotationX");
rotationY = content.GetDoubleAttribute("rotationY");
rotationZ = content.GetDoubleAttribute("rotationZ");
modelResourceName = content.GetStringAttribute("modelResourceName");
materialType = content.GetStringAttribute("materialType");
originLocation = content.GetStringAttribute("originLocation");
centerLocation = content.GetStringAttribute("centerLocation");
keepAspectRatio = content.GetBoolAttribute("keepAspectRatio");
RemoveAllAnimations();
auto &animationsElement = content.GetChild("animations");
animationsElement.ConsiderAsArrayOf("animation");
for (std::size_t i = 0; i < animationsElement.GetChildrenCount(); ++i) {
auto &animationElement = animationsElement.GetChild(i);
Model3DAnimation animation;
animation.SetName(animationElement.GetStringAttribute("name", ""));
animation.SetSource(animationElement.GetStringAttribute("source", ""));
animation.SetShouldLoop(animationElement.GetBoolAttribute("loop", false));
AddAnimation(animation);
}
}
void Model3DObjectConfiguration::DoSerializeTo(
gd::SerializerElement &element) const {
auto &content = element.AddChild("content");
content.SetAttribute("width", width);
content.SetAttribute("height", height);
content.SetAttribute("depth", depth);
content.SetAttribute("rotationX", rotationX);
content.SetAttribute("rotationY", rotationY);
content.SetAttribute("rotationZ", rotationZ);
content.SetAttribute("modelResourceName", modelResourceName);
content.SetAttribute("materialType", materialType);
content.SetAttribute("originLocation", originLocation);
content.SetAttribute("centerLocation", centerLocation);
content.SetAttribute("keepAspectRatio", keepAspectRatio);
auto &animationsElement = content.AddChild("animations");
animationsElement.ConsiderAsArrayOf("animation");
for (auto &animation : animations) {
auto &animationElement = animationsElement.AddChild("animation");
animationElement.SetAttribute("name", animation.GetName());
animationElement.SetAttribute("source", animation.GetSource());
animationElement.SetAttribute("loop", animation.ShouldLoop());
}
}
void Model3DObjectConfiguration::ExposeResources(
gd::ArbitraryResourceWorker &worker) {
worker.ExposeModel3D(modelResourceName);
}
Model3DAnimation Model3DObjectConfiguration::badAnimation;
const Model3DAnimation &
Model3DObjectConfiguration::GetAnimation(std::size_t nb) const {
if (nb >= animations.size())
return badAnimation;
return animations[nb];
}
Model3DAnimation &Model3DObjectConfiguration::GetAnimation(std::size_t nb) {
if (nb >= animations.size())
return badAnimation;
return animations[nb];
}
bool Model3DObjectConfiguration::HasAnimationNamed(
const gd::String &name) const {
return !name.empty() && (find_if(animations.begin(), animations.end(),
[&name](const Model3DAnimation &animation) {
return animation.GetName() == name;
}) != animations.end());
}
void Model3DObjectConfiguration::AddAnimation(
const Model3DAnimation &animation) {
animations.push_back(animation);
}
bool Model3DObjectConfiguration::RemoveAnimation(std::size_t nb) {
if (nb >= GetAnimationsCount())
return false;
animations.erase(animations.begin() + nb);
return true;
}
void Model3DObjectConfiguration::SwapAnimations(std::size_t firstIndex,
std::size_t secondIndex) {
if (firstIndex < animations.size() && secondIndex < animations.size() &&
firstIndex != secondIndex)
std::swap(animations[firstIndex], animations[secondIndex]);
}
void Model3DObjectConfiguration::MoveAnimation(std::size_t oldIndex,
std::size_t newIndex) {
if (oldIndex >= animations.size() || newIndex >= animations.size())
return;
auto animation = animations[oldIndex];
animations.erase(animations.begin() + oldIndex);
animations.insert(animations.begin() + newIndex, animation);
}

View File

@@ -0,0 +1,177 @@
/**
GDevelop - Particle System Extension
Copyright (c) 2010-2016 Florian Rival (Florian.Rival@gmail.com)
This project is released under the MIT License.
*/
#pragma once
#include "GDCore/Project/ObjectConfiguration.h"
namespace gd {
class InitialInstance;
class Project;
} // namespace gd
class GD_EXTENSION_API Model3DAnimation {
public:
Model3DAnimation() : shouldLoop(false) {};
virtual ~Model3DAnimation(){};
/**
* \brief Return the name of the animation
*/
const gd::String &GetName() const { return name; }
/**
* \brief Change the name of the animation
*/
void SetName(const gd::String &name_) { name = name_; }
/**
* \brief Return the name of the animation from the GLB file.
*/
const gd::String &GetSource() const { return source; }
/**
* \brief Change the name of the animation from the GLB file.
*/
void SetSource(const gd::String &source_) { source = source_; }
/**
* \brief Return true if the animation should loop.
*/
const bool ShouldLoop() const { return shouldLoop; }
/**
* \brief Change whether the animation should loop or not.
*/
void SetShouldLoop(bool shouldLoop_) { shouldLoop = shouldLoop_; }
private:
gd::String name;
gd::String source;
bool shouldLoop;
};
/**
* \brief Particle Emitter object used for storage and for the IDE.
*/
class GD_EXTENSION_API Model3DObjectConfiguration
: public gd::ObjectConfiguration {
public:
Model3DObjectConfiguration();
virtual ~Model3DObjectConfiguration(){};
virtual std::unique_ptr<gd::ObjectConfiguration> Clone() const override {
return gd::make_unique<Model3DObjectConfiguration>(*this);
}
virtual void ExposeResources(gd::ArbitraryResourceWorker &worker) override;
virtual std::map<gd::String, gd::PropertyDescriptor>
GetProperties() const override;
virtual bool UpdateProperty(const gd::String &name,
const gd::String &value) override;
virtual std::map<gd::String, gd::PropertyDescriptor>
GetInitialInstanceProperties(const gd::InitialInstance &instance,
gd::Project &project,
gd::Layout &layout) override;
virtual bool UpdateInitialInstanceProperty(gd::InitialInstance &instance,
const gd::String &name,
const gd::String &value,
gd::Project &project,
gd::Layout &layout) override;
/** \name Animations
* Methods related to animations management
*/
///@{
/**
* \brief Return the animation at the specified index.
* If the index is out of bound, a "bad animation" object is returned.
*/
const Model3DAnimation &GetAnimation(std::size_t nb) const;
/**
* \brief Return the animation at the specified index.
* If the index is out of bound, a "bad animation" object is returned.
*/
Model3DAnimation &GetAnimation(std::size_t nb);
/**
* \brief Return the number of animations this object has.
*/
std::size_t GetAnimationsCount() const { return animations.size(); };
/**
* \brief Return true if the animation called "name" exists.
*/
bool HasAnimationNamed(const gd::String& name) const;
/**
* \brief Add an animation at the end of the existing ones.
*/
void AddAnimation(const Model3DAnimation &animation);
/**
* \brief Remove an animation.
*/
bool RemoveAnimation(std::size_t nb);
/**
* \brief Remove all animations.
*/
void RemoveAllAnimations() { animations.clear(); }
/**
* \brief Return true if the object hasn't any animation.
*/
bool HasNoAnimations() const { return animations.empty(); }
/**
* \brief Swap the position of two animations
*/
void SwapAnimations(std::size_t firstIndex, std::size_t secondIndex);
/**
* \brief Change the position of the specified animation
*/
void MoveAnimation(std::size_t oldIndex, std::size_t newIndex);
/**
* \brief Return a read-only reference to the vector containing all the
* animation of the object.
*/
const std::vector<Model3DAnimation> &GetAllAnimations() const {
return animations;
}
///@}
protected:
virtual void DoUnserializeFrom(gd::Project &project,
const gd::SerializerElement &element) override;
virtual void DoSerializeTo(gd::SerializerElement &element) const override;
private:
double width;
double height;
double depth;
double rotationX;
double rotationY;
double rotationZ;
gd::String modelResourceName;
gd::String materialType;
gd::String originLocation;
gd::String centerLocation;
bool keepAspectRatio;
std::vector<Model3DAnimation> animations;
static Model3DAnimation badAnimation; //< Bad animation when an out of bound
// animation is requested.
};

View File

@@ -1,4 +1,6 @@
namespace gdjs {
type Model3DAnimation = { name: string; source: string; loop: boolean };
/** Base parameters for {@link gdjs.Cube3DRuntimeObject} */
export interface Model3DObjectData extends Object3DData {
/** The base parameters of the Model3D object */
@@ -9,9 +11,40 @@ namespace gdjs {
rotationZ: number;
keepAspectRatio: boolean;
materialType: 'Basic' | 'StandardWithoutMetalness' | 'KeepOriginal';
originLocation:
| 'ModelOrigin'
| 'ObjectCenter'
| 'BottomCenterZ'
| 'BottomCenterY'
| 'TopLeft';
centerLocation:
| 'ModelOrigin'
| 'ObjectCenter'
| 'BottomCenterZ'
| 'BottomCenterY';
animations: Model3DAnimation[];
};
}
type FloatPoint3D = [float, float, float];
const getPointForLocation = (location: string): FloatPoint3D | null => {
switch (location) {
case 'ModelOrigin':
return null;
case 'ObjectCenter':
return [0.5, 0.5, 0.5];
case 'BottomCenterZ':
return [0.5, 0.5, 0];
case 'BottomCenterY':
return [0.5, 1, 0.5];
case 'TopLeft':
return [0, 0, 0];
default:
return null;
}
};
/**
* A 3D object which displays a 3D model.
*/
@@ -22,17 +55,61 @@ namespace gdjs {
_materialType: gdjs.Model3DRuntimeObject.MaterialType =
gdjs.Model3DRuntimeObject.MaterialType.Basic;
/**
* The local point of the model that will be at the object position.
*
* Coordinates are between 0 and 1.
*
* Its value is `null` when the point is configured to `"ModelOrigin"`
* because the model origin needs to be evaluated according to the object
* configuration.
* @see gdjs.Model3DRuntimeObject3DRenderer.getOriginPoint
*/
_originPoint: FloatPoint3D | null;
/**
* The local point of the model that is used as rotation center.
*
* Coordinates are between 0 and 1.
*
* Its value is `null` when the point is configured to `"ModelOrigin"`
* because the model origin needs to be evaluated according to the object
* configuration.
* @see gdjs.Model3DRuntimeObject3DRenderer.getCenterPoint
*/
_centerPoint: FloatPoint3D | null;
_animations: Model3DAnimation[];
_currentAnimationIndex: integer = 0;
_animationSpeedScale: float = 1;
_animationPaused: boolean = false;
constructor(
instanceContainer: gdjs.RuntimeInstanceContainer,
objectData: Model3DObjectData
) {
super(instanceContainer, objectData);
this._modelResourceName = objectData.content.modelResourceName;
this._animations = objectData.content.animations;
this._originPoint = getPointForLocation(
objectData.content.originLocation
);
this._centerPoint = getPointForLocation(
objectData.content.centerLocation
);
this._renderer = new gdjs.Model3DRuntimeObjectRenderer(
this,
instanceContainer
);
this._updateMaterialType(objectData);
this._materialType = this._convertMaterialType(
objectData.content.materialType
);
this._updateModel(objectData);
if (this._animations.length > 0) {
this._renderer.playAnimation(
this._animations[0].source,
this._animations[0].loop
);
}
// *ALWAYS* call `this.onCreated()` at the very end of your object constructor.
this.onCreated();
@@ -53,23 +130,42 @@ namespace gdjs {
oldObjectData.content.keepAspectRatio !==
newObjectData.content.keepAspectRatio
) {
this._updateDefaultTransformation(newObjectData);
this._updateModel(newObjectData);
}
if (
oldObjectData.content.materialType !==
newObjectData.content.materialType
) {
this._updateMaterialType(newObjectData);
this._materialType = this._convertMaterialType(
newObjectData.content.materialType
);
this._updateModel(newObjectData);
}
if (
oldObjectData.content.originLocation !==
newObjectData.content.originLocation
) {
this._originPoint = getPointForLocation(
newObjectData.content.originLocation
);
}
if (
oldObjectData.content.centerLocation !==
newObjectData.content.centerLocation
) {
this._centerPoint = getPointForLocation(
newObjectData.content.centerLocation
);
}
return true;
}
_updateDefaultTransformation(objectData: Model3DObjectData) {
_updateModel(objectData: Model3DObjectData) {
const rotationX = objectData.content.rotationX || 0;
const rotationY = objectData.content.rotationY || 0;
const rotationZ = objectData.content.rotationZ || 0;
const keepAspectRatio = objectData.content.keepAspectRatio;
this._renderer._updateDefaultTransformation(
this._renderer._updateModel(
rotationX,
rotationY,
rotationZ,
@@ -96,12 +192,118 @@ namespace gdjs {
}
}
_updateMaterialType(objectData: Model3DObjectData) {
this._materialType = this._convertMaterialType(
objectData.content.materialType
update(instanceContainer: gdjs.RuntimeInstanceContainer): void {
const elapsedTime = this.getElapsedTime() / 1000;
this._renderer.updateAnimation(elapsedTime * this._animationSpeedScale);
}
/**
* Get the index of the animation being played.
* @return The index of the new animation being played
*/
getAnimationIndex(): number {
return this._currentAnimationIndex;
}
/**
* Change the animation being played.
* @param animationIndex The index of the new animation to be played
*/
setAnimationIndex(animationIndex: number): void {
animationIndex = animationIndex | 0;
if (
animationIndex < this._animations.length &&
this._currentAnimationIndex !== animationIndex &&
animationIndex >= 0
) {
const animation = this._animations[animationIndex];
this._currentAnimationIndex = animationIndex;
this._renderer.playAnimation(animation.source, animation.loop);
}
}
/**
* Get the name of the animation being played.
* @return The name of the new animation being played
*/
getAnimationName(): string {
if (this._currentAnimationIndex >= this._animations.length) {
return '';
}
return this._animations[this._currentAnimationIndex].name;
}
/**
* Change the animation being played.
* @param newAnimationName The name of the new animation to be played
*/
setAnimationName(newAnimationName: string): void {
if (!newAnimationName) {
return;
}
const animationIndex = this._animations.findIndex(
(animation) => animation.name === newAnimationName
);
this._renderer._updateMaterials();
this._updateDefaultTransformation(objectData);
if (animationIndex >= 0) {
this.setAnimationIndex(animationIndex);
}
}
isCurrentAnimationName(name: string): boolean {
return this.getAnimationName() === name;
}
/**
* Return true if animation has ended.
* The animation had ended if:
* - it's not configured as a loop;
* - the current frame is the last frame;
* - the last frame has been displayed long enough.
*/
hasAnimationEnded(): boolean {
return this._renderer.hasAnimationEnded();
}
isAnimationPaused() {
return this._animationPaused;
}
pauseAnimation() {
this._animationPaused = true;
return this._renderer.pauseAnimation();
}
resumeAnimation() {
this._animationPaused = false;
return this._renderer.resumeAnimation();
}
getAnimationSpeedScale() {
return this._animationSpeedScale;
}
setAnimationSpeedScale(ratio: float): void {
this._animationSpeedScale = ratio;
}
getCenterX(): float {
const centerPoint = this._renderer.getCenterPoint();
return this.getWidth() * centerPoint[0];
}
getCenterY(): float {
const centerPoint = this._renderer.getCenterPoint();
return this.getHeight() * centerPoint[1];
}
getDrawableX(): float {
const originPoint = this._renderer.getOriginPoint();
return this.getX() - this.getWidth() * originPoint[0];
}
getDrawableY(): float {
const originPoint = this._renderer.getOriginPoint();
return this.getY() - this.getHeight() * originPoint[1];
}
}

View File

@@ -1,38 +1,144 @@
namespace gdjs {
type FloatPoint3D = [float, float, float];
const removeMetalness = (material: THREE.Material): void => {
//@ts-ignore
if (material.metalness) {
//@ts-ignore
material.metalness = 0;
}
};
const removeMetalnessFromMesh = (node: THREE.Object3D<THREE.Event>) => {
const mesh = node as THREE.Mesh;
if (!mesh.material) {
return;
}
if (Array.isArray(mesh.material)) {
for (let index = 0; index < mesh.material.length; index++) {
removeMetalness(mesh.material[index]);
}
} else {
removeMetalness(mesh.material);
}
};
const traverseToRemoveMetalnessFromMeshes = (
node: THREE.Object3D<THREE.Event>
) => node.traverse(removeMetalnessFromMesh);
const convertToBasicMaterial = (
material: THREE.Material
): THREE.MeshBasicMaterial => {
const basicMaterial = new THREE.MeshBasicMaterial();
//@ts-ignore
if (material.color) {
//@ts-ignore
basicMaterial.color = material.color;
}
//@ts-ignore
if (material.map) {
//@ts-ignore
basicMaterial.map = material.map;
}
return basicMaterial;
};
const setBasicMaterialTo = (node: THREE.Object3D<THREE.Event>): void => {
const mesh = node as THREE.Mesh;
if (!mesh.material) {
return;
}
if (Array.isArray(mesh.material)) {
for (let index = 0; index < mesh.material.length; index++) {
mesh.material[index] = convertToBasicMaterial(mesh.material[index]);
}
} else {
mesh.material = convertToBasicMaterial(mesh.material);
}
};
const traverseToSetBasicMaterialFromMeshes = (
node: THREE.Object3D<THREE.Event>
) => node.traverse(setBasicMaterialTo);
class Model3DRuntimeObject3DRenderer extends gdjs.RuntimeObject3DRenderer {
private _model3DRuntimeObject: gdjs.Model3DRuntimeObject;
/**
* The 3D model stretched in a 1x1x1 cube.
*/
private _threeObject: THREE.Object3D;
private _originalModel: THREE_ADDONS.GLTF;
private _animationMixer: THREE.AnimationMixer;
private _action: THREE.AnimationAction | null;
/**
* The model origin evaluated according to the object configuration.
*
* Coordinates are between 0 and 1.
*/
private _modelOriginPoint: FloatPoint3D;
constructor(
runtimeObject: gdjs.Model3DRuntimeObject,
instanceContainer: gdjs.RuntimeInstanceContainer
) {
// @ts-ignore It can't be null if THREE exists.
const originalModelMesh: THREE.Object3D = instanceContainer
// GLB files with skeleton must not have any transformation to work properly.
const originalModel = instanceContainer
.getGame()
.getModel3DManager()
.getModel(runtimeObject._modelResourceName);
const modelObject3D = originalModelMesh.clone();
// _updateModel will actually add a clone of the model.
const model = new THREE.Group();
// Create a group to transform the object according to
// position, angle and dimensions.
const group = new THREE.Group();
group.rotation.order = 'ZYX';
group.add(modelObject3D);
group.add(model);
super(runtimeObject, instanceContainer, group);
this._model3DRuntimeObject = runtimeObject;
this._threeObject = modelObject3D;
this._threeObject = model;
this._originalModel = originalModel;
this._modelOriginPoint = [0, 0, 0];
this.updateSize();
this.updatePosition();
this.updateRotation();
this._animationMixer = new THREE.AnimationMixer(model);
this._action = null;
}
_updateDefaultTransformation(
updateAnimation(timeDelta: float) {
this._animationMixer.update(timeDelta);
}
updatePosition() {
const originPoint = this.getOriginPoint();
const centerPoint = this.getCenterPoint();
this.get3DRendererObject().position.set(
this._object.getX() -
this._object.getWidth() * (originPoint[0] - centerPoint[0]),
this._object.getY() -
this._object.getHeight() * (originPoint[1] - centerPoint[1]),
this._object.getZ() -
this._object.getDepth() * (originPoint[2] - centerPoint[2])
);
}
getOriginPoint() {
return this._model3DRuntimeObject._originPoint || this._modelOriginPoint;
}
getCenterPoint() {
return this._model3DRuntimeObject._centerPoint || this._modelOriginPoint;
}
private _updateDefaultTransformation(
threeObject: THREE.Object3D,
rotationX: float,
rotationY: float,
rotationZ: float,
@@ -41,38 +147,63 @@ namespace gdjs {
originalDepth: float,
keepAspectRatio: boolean
) {
const boundingBox = this._getModelAABB(rotationX, rotationY, rotationZ);
threeObject.rotation.set(
gdjs.toRad(rotationX),
gdjs.toRad(rotationY),
gdjs.toRad(rotationZ)
);
threeObject.updateMatrixWorld(true);
const boundingBox = new THREE.Box3().setFromObject(threeObject);
const modelWidth = boundingBox.max.x - boundingBox.min.x;
const modelHeight = boundingBox.max.y - boundingBox.min.y;
const modelDepth = boundingBox.max.z - boundingBox.min.z;
this._modelOriginPoint[0] = -boundingBox.min.x / modelWidth;
this._modelOriginPoint[1] = -boundingBox.min.y / modelHeight;
this._modelOriginPoint[2] = -boundingBox.min.z / modelDepth;
// The model is flipped on Y axis.
this._modelOriginPoint[1] = 1 - this._modelOriginPoint[1];
// Center the model.
this._threeObject.position.set(
-(boundingBox.min.x + boundingBox.max.x) / 2,
(this._threeObject.position.y =
-(boundingBox.min.y + boundingBox.max.y) / 2),
(this._threeObject.position.z =
-(boundingBox.min.z + boundingBox.max.z) / 2)
);
const centerPoint = this._model3DRuntimeObject._centerPoint;
if (centerPoint) {
threeObject.position.set(
-(
boundingBox.min.x +
(boundingBox.max.x - boundingBox.min.x) * centerPoint[0]
),
// The model is flipped on Y axis.
-(
boundingBox.min.y +
(boundingBox.max.y - boundingBox.min.y) * (1 - centerPoint[1])
),
-(
boundingBox.min.z +
(boundingBox.max.z - boundingBox.min.z) * centerPoint[2]
)
);
}
// Rotate the model.
this._threeObject.scale.set(1, 1, 1);
this._threeObject.rotation.set(
threeObject.scale.set(1, 1, 1);
threeObject.rotation.set(
gdjs.toRad(rotationX),
gdjs.toRad(rotationY),
gdjs.toRad(rotationZ)
);
// Stretch the model in a 1x1x1 cube.
const modelWidth = boundingBox.max.x - boundingBox.min.x;
const modelHeight = boundingBox.max.y - boundingBox.min.y;
const modelDepth = boundingBox.max.z - boundingBox.min.z;
const scaleX = 1 / modelWidth;
const scaleY = 1 / modelHeight;
const scaleZ = 1 / modelDepth;
const scaleMatrix = new THREE.Matrix4();
scaleMatrix.makeScale(scaleX, scaleY, scaleZ);
this._threeObject.updateMatrix();
this._threeObject.applyMatrix4(scaleMatrix);
// Flip on Y because the Y axis is on the opposite side of direct basis.
// It avoids models to be like a mirror refection.
scaleMatrix.makeScale(scaleX, -scaleY, scaleZ);
threeObject.updateMatrix();
threeObject.applyMatrix4(scaleMatrix);
if (keepAspectRatio) {
// Reduce the object dimensions to keep aspect ratio.
@@ -85,98 +216,138 @@ namespace gdjs {
this._object._setOriginalHeight(scaleRatio * modelHeight);
this._object._setOriginalDepth(scaleRatio * modelDepth);
}
this._threeObject.updateMatrix();
}
private _getModelAABB(
_updateModel(
rotationX: float,
rotationY: float,
rotationZ: float
rotationZ: float,
originalWidth: float,
originalHeight: float,
originalDepth: float,
keepAspectRatio: boolean
) {
// The original model is used because `setFromObject` is working in
// world transformation.
// Start from the original model because:
// - _replaceMaterials is destructive
// - _updateDefaultTransformation may need to work with meshes in local space
// @ts-ignore It can't be null if THREE exists.
const originalModelMesh: THREE.Object3D = this._object
.getInstanceContainer()
.getGame()
.getModel3DManager()
.getModel(this._model3DRuntimeObject._modelResourceName);
// This group hold the rotation defined by properties.
const threeObject = new THREE.Group();
threeObject.rotation.order = 'ZYX';
const root = THREE_ADDONS.SkeletonUtils.clone(this._originalModel.scene);
threeObject.add(root);
originalModelMesh.rotation.set(
gdjs.toRad(rotationX),
gdjs.toRad(rotationY),
gdjs.toRad(rotationZ)
this._replaceMaterials(threeObject);
this._updateDefaultTransformation(
threeObject,
rotationX,
rotationY,
rotationZ,
originalWidth,
originalHeight,
originalDepth,
keepAspectRatio
);
const aabb = new THREE.Box3().setFromObject(originalModelMesh);
// Revert changes.
originalModelMesh.rotation.set(0, 0, 0);
return aabb;
}
_updateMaterials() {
// @ts-ignore It can't be null if THREE exists.
const originalModelMesh: THREE.Object3D = this._model3DRuntimeObject
.getInstanceContainer()
.getGame()
.getModel3DManager()
.getModel(this._model3DRuntimeObject._modelResourceName);
const modelObject3D = originalModelMesh.clone();
// Replace the 3D object.
this.get3DRendererObject().remove(this._threeObject);
this.get3DRendererObject().add(modelObject3D);
this.get3DRendererObject().add(threeObject);
this._threeObject = threeObject;
this._threeObject = modelObject3D;
this._replaceMaterials();
// Start the current animation on the new 3D object.
this._animationMixer = new THREE.AnimationMixer(root);
const isAnimationPaused = this._model3DRuntimeObject.isAnimationPaused();
this._model3DRuntimeObject.setAnimationIndex(
this._model3DRuntimeObject.getAnimationIndex()
);
if (isAnimationPaused) {
this.pauseAnimation();
}
}
/**
* Replace materials to better work with lights (or no light).
*/
_replaceMaterials() {
private _replaceMaterials(threeObject: THREE.Object3D) {
if (
this._model3DRuntimeObject._materialType ===
gdjs.Model3DRuntimeObject.MaterialType.StandardWithoutMetalness
) {
this._threeObject.traverse((node) => {
if (node.type === 'Mesh') {
const mesh = node as THREE.Mesh;
const material = mesh.material as THREE.MeshStandardMaterial;
//@ts-ignore
if (material.metalness) {
//@ts-ignore
material.metalness = 0;
}
}
});
traverseToRemoveMetalnessFromMeshes(threeObject);
} else if (
this._model3DRuntimeObject._materialType ===
gdjs.Model3DRuntimeObject.MaterialType.Basic
) {
this._threeObject.traverse((node) => {
if (node.type === 'Mesh') {
const mesh = node as THREE.Mesh;
const basicMaterial = new THREE.MeshBasicMaterial();
//@ts-ignore
if (mesh.material.color) {
//@ts-ignore
basicMaterial.color = mesh.material.color;
}
//@ts-ignore
if (mesh.material.map) {
//@ts-ignore
basicMaterial.map = mesh.material.map;
}
mesh.material = basicMaterial;
}
});
traverseToSetBasicMaterialFromMeshes(threeObject);
}
}
getAnimationCount() {
return this._originalModel.animations.length;
}
getAnimationName(animationIndex: integer) {
return this._originalModel.animations[animationIndex].name;
}
/**
* Return true if animation has ended.
* The animation had ended if:
* - it's not configured as a loop;
* - the current frame is the last frame;
* - the last frame has been displayed long enough.
*/
hasAnimationEnded(): boolean {
if (!this._action) {
return true;
}
return !this._action.isRunning();
}
animationPaused() {
if (!this._action) {
return;
}
return this._action.paused;
}
pauseAnimation() {
if (!this._action) {
return;
}
this._action.paused = true;
}
resumeAnimation() {
if (!this._action) {
return;
}
this._action.paused = false;
}
playAnimation(animationName: string, shouldLoop: boolean) {
this._animationMixer.stopAllAction();
const clip = THREE.AnimationClip.findByName(
this._originalModel.animations,
animationName
);
if (!clip) {
console.error(
`The GLB file: ${this._model3DRuntimeObject._modelResourceName} doesn't have any animation named: ${animationName}`
);
return;
}
this._action = this._animationMixer.clipAction(clip);
this._action.setLoop(
shouldLoop ? THREE.LoopRepeat : THREE.LoopOnce,
Number.POSITIVE_INFINITY
);
this._action.clampWhenFinished = true;
this._action.play();
// Make sure the first frame is displayed.
this._animationMixer.update(0);
}
}
export const Model3DRuntimeObjectRenderer = Model3DRuntimeObject3DRenderer;

View File

@@ -8,6 +8,7 @@ project(GD-Extensions)
include(CMakeUtils.txt) #Functions to factor common tasks done in CMakeLists.txt of extensions
#Add all the CMakeLists (for non pure JS extensions):
ADD_SUBDIRECTORY(3D)
ADD_SUBDIRECTORY(AnchorBehavior)
ADD_SUBDIRECTORY(DestroyOutsideBehavior)
ADD_SUBDIRECTORY(DraggableBehavior)

View File

@@ -46,7 +46,7 @@ module.exports = {
_(
'Load a dialogue data object - Yarn json format, stored in a scene variable. Use this command to load all the Dialogue data at the beginning of the game.'
),
_('Load dialogue data from Scene variable _PARAM1_'),
_('Load dialogue data from Scene variable _PARAM0_'),
'',
'JsPlatform/Extensions/yarn32.png',
'JsPlatform/Extensions/yarn32.png'

View File

@@ -18,6 +18,7 @@ export type ObjectsRenderingService = {
gd: libGDevelop,
PIXI: any,
THREE: any,
THREE_ADDONS: {SkeletonUtils: any},
RenderedInstance: any,
Rendered3DInstance: any,
registerInstanceRenderer: (objectType: string, renderer: any) => void,

View File

@@ -290,74 +290,31 @@ module.exports = {
.getValue()
);
if (this._radius <= 0) this._radius = 1;
const colorHex = objectsRenderingService.rgbOrHexToHexNumber(
const color = objectsRenderingService.rgbOrHexToHexNumber(
this._associatedObjectConfiguration
.getProperties(this.project)
.get('color')
.getValue()
);
this._color = [
((colorHex >> 16) & 0xff) / 255,
((colorHex >> 8) & 0xff) / 255,
(colorHex & 0xff) / 255,
];
const geometry = new PIXI.Geometry();
const shader = PIXI.Shader.from(
`
precision mediump float;
attribute vec2 aVertexPosition;
// The icon in the middle.
const lightIconSprite = new PIXI.Sprite(PIXI.Texture.from('CppPlatform/Extensions/lightIcon32.png'));
lightIconSprite.anchor.x = 0.5;
lightIconSprite.anchor.y = 0.5;
uniform mat3 translationMatrix;
uniform mat3 projectionMatrix;
varying vec2 vPos;
void main() {
vPos = aVertexPosition;
gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);
}`,
`
precision mediump float;
uniform vec2 center;
uniform float radius;
uniform vec3 color;
uniform mat3 translationMatrix;
uniform mat3 projectionMatrix;
varying vec2 vPos;
void main() {
float l = length(vPos - center);
float intensity = 0.0;
if(l < radius)
intensity = clamp((radius - l)*(radius - l)/(radius*radius), 0.0, 1.0);
gl_FragColor = vec4(color*intensity, 1.0);
}
`,
{
center: [this._instance.getX(), this._instance.getY()],
radius: this._radius,
color: this._color,
}
// The circle to show the radius of the light.
const radiusBorderWidth = 2;
const radiusGraphics = new PIXI.Graphics();
radiusGraphics.lineStyle(
radiusBorderWidth,
color,
0.8
);
radiusGraphics.drawCircle(0, 0, Math.max(1, this._radius - radiusBorderWidth));
this._vertexBuffer = new Float32Array([
this._instance.getX() - this._radius,
this._instance.getY() + this._radius,
this._instance.getX() + this._radius,
this._instance.getY() + this._radius,
this._instance.getX() + this._radius,
this._instance.getY() - this._radius,
this._instance.getX() - this._radius,
this._instance.getY() - this._radius,
]);
geometry
.addAttribute('aVertexPosition', this._vertexBuffer, 2)
.addIndex([0, 1, 2, 2, 3, 0]);
this._pixiObject = new PIXI.Mesh(geometry, shader);
this._pixiObject.blendMode = PIXI.BLEND_MODES.ADD;
this._pixiObject = new PIXI.Container();
this._pixiObject.addChild(lightIconSprite);
this._pixiObject.addChild(radiusGraphics);
this._pixiContainer.addChild(this._pixiObject);
this.update();
}
@@ -380,37 +337,22 @@ module.exports = {
* This is called to update the PIXI object on the scene editor
*/
RenderedLightObjectInstance.prototype.update = function () {
this._pixiObject.shader.uniforms.center = new Float32Array([
this._instance.getX(),
this._instance.getY(),
]);
this._vertexBuffer[0] = this._instance.getX() - this._radius;
this._vertexBuffer[1] = this._instance.getY() + this._radius;
this._vertexBuffer[2] = this._instance.getX() + this._radius;
this._vertexBuffer[3] = this._instance.getY() + this._radius;
this._vertexBuffer[4] = this._instance.getX() + this._radius;
this._vertexBuffer[5] = this._instance.getY() - this._radius;
this._vertexBuffer[6] = this._instance.getX() - this._radius;
this._vertexBuffer[7] = this._instance.getY() - this._radius;
this._pixiObject.geometry
.getBuffer('aVertexPosition')
.update(this._vertexBuffer);
this._pixiObject.position.x = this._instance.getX();
this._pixiObject.position.y = this._instance.getY();
};
/**
* Return the width of the instance, when it's not resized.
*/
RenderedLightObjectInstance.prototype.getDefaultWidth = function () {
return this._pixiObject.width;
return this._radius * 2;
};
/**
* Return the height of the instance, when it's not resized.
*/
RenderedLightObjectInstance.prototype.getDefaultHeight = function () {
return this._pixiObject.height;
return this._radius * 2;
};
RenderedLightObjectInstance.prototype.getOriginX = function () {

View File

@@ -5,47 +5,7 @@
namespace gdjs {
export namespace evtTools {
export namespace systemInfo {
let cachedIsMobile: boolean | null = null;
let cachedHasTouchScreen: boolean | null = null;
const checkIsMobile = (): boolean => {
if (typeof cc !== 'undefined' && cc.sys) {
return cc.sys.isMobile;
}
// @ts-expect-error ts-migrate(2304) FIXME: Cannot find name 'Cocoon'.
else if (typeof Cocoon !== 'undefined' && Cocoon.App) {
return true;
} else if (typeof window !== 'undefined' && (window as any).cordova) {
return true;
} else if (typeof window !== 'undefined') {
// Try to detect mobile device browsers.
if (
/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|ipad|iris|kindle|Android|Silk|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(
navigator.userAgent
) ||
/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(
navigator.userAgent.substr(0, 4)
)
) {
return true;
}
// Try to detect iOS
if (/iPad|iPhone|iPod/.test(navigator.platform)) {
return true;
} else {
if (/MacIntel/.test(navigator.platform)) {
// Work around for recent iPads that are "desktop-class browsing".
// We can still detect them using their touchscreen, but this is a hack.
// If mac laptops start to support touchscreens, this won't work anymore. Hence it's better
// to test for the presence of a touchscreen if needed rather than checking if the device
// is "mobile".
return !!navigator.maxTouchPoints && navigator.maxTouchPoints > 2;
}
}
}
return false;
};
/**
* Check if the game runs on a mobile device (iPhone, iPad, Android).
@@ -54,10 +14,7 @@ namespace gdjs {
* prefer to check if the device has touchscreen support.
*/
export const isMobile = (): boolean => {
if (cachedIsMobile !== null) {
return cachedIsMobile;
}
return (cachedIsMobile = checkIsMobile());
return gdjs.evtTools.common.isMobile();
};
/**

View File

@@ -686,6 +686,8 @@ void ExporterHelper::AddLibsInclude(bool pixiRenderers,
if (pixiInThreeRenderers) {
InsertUnique(includesFiles, "pixi-renderers/three.js");
InsertUnique(includesFiles, "pixi-renderers/ThreeAddons.js");
InsertUnique(includesFiles, "pixi-renderers/draco/gltf/draco_decoder.wasm");
InsertUnique(includesFiles, "pixi-renderers/draco/gltf/draco_wasm_wrapper.js");
}
if (pixiRenderers) {
InsertUnique(includesFiles, "pixi-renderers/pixi.js");

View File

@@ -16,14 +16,16 @@ namespace gdjs {
/**
* Map associating a resource name to the loaded Three.js model.
*/
private _loadedThreeModels = new Map<String, THREE.Object3D>();
private _loadedThreeModels = new Map<String, THREE_ADDONS.GLTF>();
_resourcesLoader: RuntimeGameResourcesLoader;
_resources: ResourceData[];
_loader: THREE_ADDONS.GLTFLoader | null = null;
_dracoLoader: THREE_ADDONS.DRACOLoader | null = null;
_invalidModel: THREE.Object3D | null = null;
//@ts-ignore Can only be null if THREE is not loaded.
_invalidModel: THREE_ADDONS.GLTF;
/**
* @param resources The resources data of the game.
@@ -39,13 +41,31 @@ namespace gdjs {
if (typeof THREE !== 'undefined') {
this._loader = new THREE_ADDONS.GLTFLoader();
this._dracoLoader = new THREE_ADDONS.DRACOLoader();
this._dracoLoader.setDecoderPath('./pixi-renderers/draco/gltf/');
this._loader.setDRACOLoader(this._dracoLoader);
/**
* The invalid model is a box with magenta (#ff00ff) faces, to be
* easily spotted if rendered on screen.
*/
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshBasicMaterial({ color: '#ff00ff' });
this._invalidModel = new THREE.Mesh(geometry, material);
const group = new THREE.Group();
group.add(
new THREE.Mesh(
new THREE.BoxGeometry(1, 1, 1),
new THREE.MeshBasicMaterial({ color: '#ff00ff' })
)
);
this._invalidModel = {
scene: group,
animations: [],
cameras: [],
scenes: [],
asset: {},
userData: {},
//@ts-ignore
parser: null,
};
}
}
@@ -89,9 +109,8 @@ namespace gdjs {
);
this._loader.load(
url,
(gltf) => {
gltf.scene.rotation.order = 'ZYX';
this._loadedThreeModels.set(resource.name, gltf.scene);
(gltf: THREE_ADDONS.GLTF) => {
this._loadedThreeModels.set(resource.name, gltf);
loaded++;
if (loaded === model3DResources.length) {
@@ -123,7 +142,7 @@ namespace gdjs {
* @param resourceName The name of the json resource.
* @returns a 3D model if it exists.
*/
getModel(resourceName: string): THREE.Object3D | null {
getModel(resourceName: string): THREE_ADDONS.GLTF {
return this._loadedThreeModels.get(resourceName) || this._invalidModel;
}
}

View File

@@ -257,8 +257,19 @@ namespace gdjs {
newObject.persistentUuid = instanceData.persistentUuid || null;
}
newObject.setPosition(instanceData.x + xPos, instanceData.y + yPos);
newObject.setZOrder(instanceData.zOrder);
newObject.setAngle(instanceData.angle);
if (
gdjs.RuntimeObject3D &&
newObject instanceof gdjs.RuntimeObject3D
) {
if (instanceData.z !== undefined) newObject.setZ(instanceData.z);
if (instanceData.rotationX !== undefined)
newObject.setRotationX(instanceData.rotationX);
if (instanceData.rotationY !== undefined)
newObject.setRotationY(instanceData.rotationY);
}
newObject.setZOrder(instanceData.zOrder);
newObject.setLayer(instanceData.layer);
newObject
.getVariables()

View File

@@ -1252,6 +1252,29 @@ namespace gdjs {
runtimeObject.setLayer(newInstance.layer);
somethingChanged = true;
}
if (
gdjs.RuntimeObject3D &&
runtimeObject instanceof gdjs.RuntimeObject3D
) {
if (oldInstance.z !== newInstance.z && newInstance.z !== undefined) {
runtimeObject.setZ(newInstance.z);
somethingChanged = true;
}
if (
oldInstance.rotationX !== newInstance.rotationX &&
newInstance.rotationX !== undefined
) {
runtimeObject.setRotationX(newInstance.rotationX);
somethingChanged = true;
}
if (
oldInstance.rotationY !== newInstance.rotationY &&
newInstance.rotationY !== undefined
) {
runtimeObject.setRotationY(newInstance.rotationY);
somethingChanged = true;
}
}
// Check if size changed
let sizeChanged = false;
@@ -1283,6 +1306,28 @@ namespace gdjs {
sizeChanged = true;
}
}
if (
gdjs.RuntimeObject3D &&
runtimeObject instanceof gdjs.RuntimeObject3D
) {
// A custom depth was set or changed
if (
oldInstance.depth !== newInstance.depth &&
newInstance.depth !== undefined
) {
runtimeObject.setDepth(newInstance.depth);
somethingChanged = true;
sizeChanged = true;
} else if (
newInstance.depth === undefined &&
oldInstance.depth !== undefined
) {
// The custom depth was removed. Just flag the depth as changed
// and hope the object will handle this in
// `extraInitializationFromInitialInstance`.
sizeChanged = true;
}
}
// Update variables
this._hotReloadVariablesContainer(

View File

@@ -396,6 +396,60 @@ namespace gdjs {
)
eventsFunctionContext.task.resolve();
};
const checkIsMobile = (): boolean => {
if (typeof cc !== 'undefined' && cc.sys) {
return cc.sys.isMobile;
}
// @ts-expect-error ts-migrate(2304) FIXME: Cannot find name 'Cocoon'.
else if (typeof Cocoon !== 'undefined' && Cocoon.App) {
return true;
} else if (typeof window !== 'undefined' && (window as any).cordova) {
return true;
} else if (typeof window !== 'undefined') {
// Try to detect mobile device browsers.
if (
/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|ipad|iris|kindle|Android|Silk|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(
navigator.userAgent
) ||
/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(
navigator.userAgent.substr(0, 4)
)
) {
return true;
}
// Try to detect iOS
if (/iPad|iPhone|iPod/.test(navigator.platform)) {
return true;
} else {
if (/MacIntel/.test(navigator.platform)) {
// Work around for recent iPads that are "desktop-class browsing".
// We can still detect them using their touchscreen, but this is a hack.
// If mac laptops start to support touchscreens, this won't work anymore. Hence it's better
// to test for the presence of a touchscreen if needed rather than checking if the device
// is "mobile".
return !!navigator.maxTouchPoints && navigator.maxTouchPoints > 2;
}
}
}
return false;
};
let cachedIsMobile: boolean | null = null;
/**
* Check if the game runs on a mobile device (iPhone, iPad, Android).
* Note that the distinction between what is a mobile device and what is not
* is becoming blurry. If you use this for mobile controls,
* prefer to check if the device has touchscreen support.
*/
export const isMobile = (): boolean => {
if (cachedIsMobile !== null) {
return cachedIsMobile;
}
return (cachedIsMobile = checkIsMobile());
};
}
}
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,116 @@
var $jscomp=$jscomp||{};$jscomp.scope={};$jscomp.arrayIteratorImpl=function(h){var n=0;return function(){return n<h.length?{done:!1,value:h[n++]}:{done:!0}}};$jscomp.arrayIterator=function(h){return{next:$jscomp.arrayIteratorImpl(h)}};$jscomp.makeIterator=function(h){var n="undefined"!=typeof Symbol&&Symbol.iterator&&h[Symbol.iterator];return n?n.call(h):$jscomp.arrayIterator(h)};$jscomp.ASSUME_ES5=!1;$jscomp.ASSUME_NO_NATIVE_MAP=!1;$jscomp.ASSUME_NO_NATIVE_SET=!1;$jscomp.SIMPLE_FROUND_POLYFILL=!1;
$jscomp.ISOLATE_POLYFILLS=!1;$jscomp.FORCE_POLYFILL_PROMISE=!1;$jscomp.FORCE_POLYFILL_PROMISE_WHEN_NO_UNHANDLED_REJECTION=!1;$jscomp.getGlobal=function(h){h=["object"==typeof globalThis&&globalThis,h,"object"==typeof window&&window,"object"==typeof self&&self,"object"==typeof global&&global];for(var n=0;n<h.length;++n){var k=h[n];if(k&&k.Math==Math)return k}throw Error("Cannot find global object");};$jscomp.global=$jscomp.getGlobal(this);
$jscomp.defineProperty=$jscomp.ASSUME_ES5||"function"==typeof Object.defineProperties?Object.defineProperty:function(h,n,k){if(h==Array.prototype||h==Object.prototype)return h;h[n]=k.value;return h};$jscomp.IS_SYMBOL_NATIVE="function"===typeof Symbol&&"symbol"===typeof Symbol("x");$jscomp.TRUST_ES6_POLYFILLS=!$jscomp.ISOLATE_POLYFILLS||$jscomp.IS_SYMBOL_NATIVE;$jscomp.polyfills={};$jscomp.propertyToPolyfillSymbol={};$jscomp.POLYFILL_PREFIX="$jscp$";
var $jscomp$lookupPolyfilledValue=function(h,n){var k=$jscomp.propertyToPolyfillSymbol[n];if(null==k)return h[n];k=h[k];return void 0!==k?k:h[n]};$jscomp.polyfill=function(h,n,k,p){n&&($jscomp.ISOLATE_POLYFILLS?$jscomp.polyfillIsolated(h,n,k,p):$jscomp.polyfillUnisolated(h,n,k,p))};
$jscomp.polyfillUnisolated=function(h,n,k,p){k=$jscomp.global;h=h.split(".");for(p=0;p<h.length-1;p++){var l=h[p];if(!(l in k))return;k=k[l]}h=h[h.length-1];p=k[h];n=n(p);n!=p&&null!=n&&$jscomp.defineProperty(k,h,{configurable:!0,writable:!0,value:n})};
$jscomp.polyfillIsolated=function(h,n,k,p){var l=h.split(".");h=1===l.length;p=l[0];p=!h&&p in $jscomp.polyfills?$jscomp.polyfills:$jscomp.global;for(var y=0;y<l.length-1;y++){var f=l[y];if(!(f in p))return;p=p[f]}l=l[l.length-1];k=$jscomp.IS_SYMBOL_NATIVE&&"es6"===k?p[l]:null;n=n(k);null!=n&&(h?$jscomp.defineProperty($jscomp.polyfills,l,{configurable:!0,writable:!0,value:n}):n!==k&&(void 0===$jscomp.propertyToPolyfillSymbol[l]&&(k=1E9*Math.random()>>>0,$jscomp.propertyToPolyfillSymbol[l]=$jscomp.IS_SYMBOL_NATIVE?
$jscomp.global.Symbol(l):$jscomp.POLYFILL_PREFIX+k+"$"+l),$jscomp.defineProperty(p,$jscomp.propertyToPolyfillSymbol[l],{configurable:!0,writable:!0,value:n})))};
$jscomp.polyfill("Promise",function(h){function n(){this.batch_=null}function k(f){return f instanceof l?f:new l(function(q,u){q(f)})}if(h&&(!($jscomp.FORCE_POLYFILL_PROMISE||$jscomp.FORCE_POLYFILL_PROMISE_WHEN_NO_UNHANDLED_REJECTION&&"undefined"===typeof $jscomp.global.PromiseRejectionEvent)||!$jscomp.global.Promise||-1===$jscomp.global.Promise.toString().indexOf("[native code]")))return h;n.prototype.asyncExecute=function(f){if(null==this.batch_){this.batch_=[];var q=this;this.asyncExecuteFunction(function(){q.executeBatch_()})}this.batch_.push(f)};
var p=$jscomp.global.setTimeout;n.prototype.asyncExecuteFunction=function(f){p(f,0)};n.prototype.executeBatch_=function(){for(;this.batch_&&this.batch_.length;){var f=this.batch_;this.batch_=[];for(var q=0;q<f.length;++q){var u=f[q];f[q]=null;try{u()}catch(A){this.asyncThrow_(A)}}}this.batch_=null};n.prototype.asyncThrow_=function(f){this.asyncExecuteFunction(function(){throw f;})};var l=function(f){this.state_=0;this.result_=void 0;this.onSettledCallbacks_=[];this.isRejectionHandled_=!1;var q=this.createResolveAndReject_();
try{f(q.resolve,q.reject)}catch(u){q.reject(u)}};l.prototype.createResolveAndReject_=function(){function f(A){return function(F){u||(u=!0,A.call(q,F))}}var q=this,u=!1;return{resolve:f(this.resolveTo_),reject:f(this.reject_)}};l.prototype.resolveTo_=function(f){if(f===this)this.reject_(new TypeError("A Promise cannot resolve to itself"));else if(f instanceof l)this.settleSameAsPromise_(f);else{a:switch(typeof f){case "object":var q=null!=f;break a;case "function":q=!0;break a;default:q=!1}q?this.resolveToNonPromiseObj_(f):
this.fulfill_(f)}};l.prototype.resolveToNonPromiseObj_=function(f){var q=void 0;try{q=f.then}catch(u){this.reject_(u);return}"function"==typeof q?this.settleSameAsThenable_(q,f):this.fulfill_(f)};l.prototype.reject_=function(f){this.settle_(2,f)};l.prototype.fulfill_=function(f){this.settle_(1,f)};l.prototype.settle_=function(f,q){if(0!=this.state_)throw Error("Cannot settle("+f+", "+q+"): Promise already settled in state"+this.state_);this.state_=f;this.result_=q;2===this.state_&&this.scheduleUnhandledRejectionCheck_();
this.executeOnSettledCallbacks_()};l.prototype.scheduleUnhandledRejectionCheck_=function(){var f=this;p(function(){if(f.notifyUnhandledRejection_()){var q=$jscomp.global.console;"undefined"!==typeof q&&q.error(f.result_)}},1)};l.prototype.notifyUnhandledRejection_=function(){if(this.isRejectionHandled_)return!1;var f=$jscomp.global.CustomEvent,q=$jscomp.global.Event,u=$jscomp.global.dispatchEvent;if("undefined"===typeof u)return!0;"function"===typeof f?f=new f("unhandledrejection",{cancelable:!0}):
"function"===typeof q?f=new q("unhandledrejection",{cancelable:!0}):(f=$jscomp.global.document.createEvent("CustomEvent"),f.initCustomEvent("unhandledrejection",!1,!0,f));f.promise=this;f.reason=this.result_;return u(f)};l.prototype.executeOnSettledCallbacks_=function(){if(null!=this.onSettledCallbacks_){for(var f=0;f<this.onSettledCallbacks_.length;++f)y.asyncExecute(this.onSettledCallbacks_[f]);this.onSettledCallbacks_=null}};var y=new n;l.prototype.settleSameAsPromise_=function(f){var q=this.createResolveAndReject_();
f.callWhenSettled_(q.resolve,q.reject)};l.prototype.settleSameAsThenable_=function(f,q){var u=this.createResolveAndReject_();try{f.call(q,u.resolve,u.reject)}catch(A){u.reject(A)}};l.prototype.then=function(f,q){function u(w,B){return"function"==typeof w?function(R){try{A(w(R))}catch(Z){F(Z)}}:B}var A,F,v=new l(function(w,B){A=w;F=B});this.callWhenSettled_(u(f,A),u(q,F));return v};l.prototype.catch=function(f){return this.then(void 0,f)};l.prototype.callWhenSettled_=function(f,q){function u(){switch(A.state_){case 1:f(A.result_);
break;case 2:q(A.result_);break;default:throw Error("Unexpected state: "+A.state_);}}var A=this;null==this.onSettledCallbacks_?y.asyncExecute(u):this.onSettledCallbacks_.push(u);this.isRejectionHandled_=!0};l.resolve=k;l.reject=function(f){return new l(function(q,u){u(f)})};l.race=function(f){return new l(function(q,u){for(var A=$jscomp.makeIterator(f),F=A.next();!F.done;F=A.next())k(F.value).callWhenSettled_(q,u)})};l.all=function(f){var q=$jscomp.makeIterator(f),u=q.next();return u.done?k([]):new l(function(A,
F){function v(R){return function(Z){w[R]=Z;B--;0==B&&A(w)}}var w=[],B=0;do w.push(void 0),B++,k(u.value).callWhenSettled_(v(w.length-1),F),u=q.next();while(!u.done)})};return l},"es6","es3");$jscomp.owns=function(h,n){return Object.prototype.hasOwnProperty.call(h,n)};$jscomp.assign=$jscomp.TRUST_ES6_POLYFILLS&&"function"==typeof Object.assign?Object.assign:function(h,n){for(var k=1;k<arguments.length;k++){var p=arguments[k];if(p)for(var l in p)$jscomp.owns(p,l)&&(h[l]=p[l])}return h};
$jscomp.polyfill("Object.assign",function(h){return h||$jscomp.assign},"es6","es3");$jscomp.checkStringArgs=function(h,n,k){if(null==h)throw new TypeError("The 'this' value for String.prototype."+k+" must not be null or undefined");if(n instanceof RegExp)throw new TypeError("First argument to String.prototype."+k+" must not be a regular expression");return h+""};
$jscomp.polyfill("String.prototype.startsWith",function(h){return h?h:function(n,k){var p=$jscomp.checkStringArgs(this,n,"startsWith");n+="";var l=p.length,y=n.length;k=Math.max(0,Math.min(k|0,p.length));for(var f=0;f<y&&k<l;)if(p[k++]!=n[f++])return!1;return f>=y}},"es6","es3");
$jscomp.polyfill("Array.prototype.copyWithin",function(h){function n(k){k=Number(k);return Infinity===k||-Infinity===k?k:k|0}return h?h:function(k,p,l){var y=this.length;k=n(k);p=n(p);l=void 0===l?y:n(l);k=0>k?Math.max(y+k,0):Math.min(k,y);p=0>p?Math.max(y+p,0):Math.min(p,y);l=0>l?Math.max(y+l,0):Math.min(l,y);if(k<p)for(;p<l;)p in this?this[k++]=this[p++]:(delete this[k++],p++);else for(l=Math.min(l,y+p-k),k+=l-p;l>p;)--l in this?this[--k]=this[l]:delete this[--k];return this}},"es6","es3");
$jscomp.typedArrayCopyWithin=function(h){return h?h:Array.prototype.copyWithin};$jscomp.polyfill("Int8Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Uint8Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Uint8ClampedArray.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Int16Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");
$jscomp.polyfill("Uint16Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Int32Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Uint32Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Float32Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Float64Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");
var DracoDecoderModule=function(){var h="undefined"!==typeof document&&document.currentScript?document.currentScript.src:void 0;"undefined"!==typeof __filename&&(h=h||__filename);return function(n){function k(e){return a.locateFile?a.locateFile(e,U):U+e}function p(e,b){if(e){var c=ia;var d=e+b;for(b=e;c[b]&&!(b>=d);)++b;if(16<b-e&&c.buffer&&ra)c=ra.decode(c.subarray(e,b));else{for(d="";e<b;){var g=c[e++];if(g&128){var t=c[e++]&63;if(192==(g&224))d+=String.fromCharCode((g&31)<<6|t);else{var aa=c[e++]&
63;g=224==(g&240)?(g&15)<<12|t<<6|aa:(g&7)<<18|t<<12|aa<<6|c[e++]&63;65536>g?d+=String.fromCharCode(g):(g-=65536,d+=String.fromCharCode(55296|g>>10,56320|g&1023))}}else d+=String.fromCharCode(g)}c=d}}else c="";return c}function l(){var e=ja.buffer;a.HEAP8=W=new Int8Array(e);a.HEAP16=new Int16Array(e);a.HEAP32=ca=new Int32Array(e);a.HEAPU8=ia=new Uint8Array(e);a.HEAPU16=new Uint16Array(e);a.HEAPU32=Y=new Uint32Array(e);a.HEAPF32=new Float32Array(e);a.HEAPF64=new Float64Array(e)}function y(e){if(a.onAbort)a.onAbort(e);
e="Aborted("+e+")";da(e);sa=!0;e=new WebAssembly.RuntimeError(e+". Build with -sASSERTIONS for more info.");ka(e);throw e;}function f(e){try{if(e==P&&ea)return new Uint8Array(ea);if(ma)return ma(e);throw"both async and sync fetching of the wasm failed";}catch(b){y(b)}}function q(){if(!ea&&(ta||fa)){if("function"==typeof fetch&&!P.startsWith("file://"))return fetch(P,{credentials:"same-origin"}).then(function(e){if(!e.ok)throw"failed to load wasm binary file at '"+P+"'";return e.arrayBuffer()}).catch(function(){return f(P)});
if(na)return new Promise(function(e,b){na(P,function(c){e(new Uint8Array(c))},b)})}return Promise.resolve().then(function(){return f(P)})}function u(e){for(;0<e.length;)e.shift()(a)}function A(e){this.excPtr=e;this.ptr=e-24;this.set_type=function(b){Y[this.ptr+4>>2]=b};this.get_type=function(){return Y[this.ptr+4>>2]};this.set_destructor=function(b){Y[this.ptr+8>>2]=b};this.get_destructor=function(){return Y[this.ptr+8>>2]};this.set_refcount=function(b){ca[this.ptr>>2]=b};this.set_caught=function(b){W[this.ptr+
12>>0]=b?1:0};this.get_caught=function(){return 0!=W[this.ptr+12>>0]};this.set_rethrown=function(b){W[this.ptr+13>>0]=b?1:0};this.get_rethrown=function(){return 0!=W[this.ptr+13>>0]};this.init=function(b,c){this.set_adjusted_ptr(0);this.set_type(b);this.set_destructor(c);this.set_refcount(0);this.set_caught(!1);this.set_rethrown(!1)};this.add_ref=function(){ca[this.ptr>>2]+=1};this.release_ref=function(){var b=ca[this.ptr>>2];ca[this.ptr>>2]=b-1;return 1===b};this.set_adjusted_ptr=function(b){Y[this.ptr+
16>>2]=b};this.get_adjusted_ptr=function(){return Y[this.ptr+16>>2]};this.get_exception_ptr=function(){if(ua(this.get_type()))return Y[this.excPtr>>2];var b=this.get_adjusted_ptr();return 0!==b?b:this.excPtr}}function F(){function e(){if(!la&&(la=!0,a.calledRun=!0,!sa)){va=!0;u(oa);wa(a);if(a.onRuntimeInitialized)a.onRuntimeInitialized();if(a.postRun)for("function"==typeof a.postRun&&(a.postRun=[a.postRun]);a.postRun.length;)xa.unshift(a.postRun.shift());u(xa)}}if(!(0<ba)){if(a.preRun)for("function"==
typeof a.preRun&&(a.preRun=[a.preRun]);a.preRun.length;)ya.unshift(a.preRun.shift());u(ya);0<ba||(a.setStatus?(a.setStatus("Running..."),setTimeout(function(){setTimeout(function(){a.setStatus("")},1);e()},1)):e())}}function v(){}function w(e){return(e||v).__cache__}function B(e,b){var c=w(b),d=c[e];if(d)return d;d=Object.create((b||v).prototype);d.ptr=e;return c[e]=d}function R(e){if("string"===typeof e){for(var b=0,c=0;c<e.length;++c){var d=e.charCodeAt(c);127>=d?b++:2047>=d?b+=2:55296<=d&&57343>=
d?(b+=4,++c):b+=3}b=Array(b+1);c=0;d=b.length;if(0<d){d=c+d-1;for(var g=0;g<e.length;++g){var t=e.charCodeAt(g);if(55296<=t&&57343>=t){var aa=e.charCodeAt(++g);t=65536+((t&1023)<<10)|aa&1023}if(127>=t){if(c>=d)break;b[c++]=t}else{if(2047>=t){if(c+1>=d)break;b[c++]=192|t>>6}else{if(65535>=t){if(c+2>=d)break;b[c++]=224|t>>12}else{if(c+3>=d)break;b[c++]=240|t>>18;b[c++]=128|t>>12&63}b[c++]=128|t>>6&63}b[c++]=128|t&63}}b[c]=0}e=r.alloc(b,W);r.copy(b,W,e);return e}return e}function Z(e){if("object"===
typeof e){var b=r.alloc(e,W);r.copy(e,W,b);return b}return e}function X(){throw"cannot construct a VoidPtr, no constructor in IDL";}function S(){this.ptr=za();w(S)[this.ptr]=this}function Q(){this.ptr=Aa();w(Q)[this.ptr]=this}function V(){this.ptr=Ba();w(V)[this.ptr]=this}function x(){this.ptr=Ca();w(x)[this.ptr]=this}function D(){this.ptr=Da();w(D)[this.ptr]=this}function G(){this.ptr=Ea();w(G)[this.ptr]=this}function H(){this.ptr=Fa();w(H)[this.ptr]=this}function E(){this.ptr=Ga();w(E)[this.ptr]=
this}function T(){this.ptr=Ha();w(T)[this.ptr]=this}function C(){throw"cannot construct a Status, no constructor in IDL";}function I(){this.ptr=Ia();w(I)[this.ptr]=this}function J(){this.ptr=Ja();w(J)[this.ptr]=this}function K(){this.ptr=Ka();w(K)[this.ptr]=this}function L(){this.ptr=La();w(L)[this.ptr]=this}function M(){this.ptr=Ma();w(M)[this.ptr]=this}function N(){this.ptr=Na();w(N)[this.ptr]=this}function O(){this.ptr=Oa();w(O)[this.ptr]=this}function z(){this.ptr=Pa();w(z)[this.ptr]=this}function m(){this.ptr=
Qa();w(m)[this.ptr]=this}n=void 0===n?{}:n;var a="undefined"!=typeof n?n:{},wa,ka;a.ready=new Promise(function(e,b){wa=e;ka=b});var Ra=!1,Sa=!1;a.onRuntimeInitialized=function(){Ra=!0;if(Sa&&"function"===typeof a.onModuleLoaded)a.onModuleLoaded(a)};a.onModuleParsed=function(){Sa=!0;if(Ra&&"function"===typeof a.onModuleLoaded)a.onModuleLoaded(a)};a.isVersionSupported=function(e){if("string"!==typeof e)return!1;e=e.split(".");return 2>e.length||3<e.length?!1:1==e[0]&&0<=e[1]&&5>=e[1]?!0:0!=e[0]||10<
e[1]?!1:!0};var Ta=Object.assign({},a),ta="object"==typeof window,fa="function"==typeof importScripts,Ua="object"==typeof process&&"object"==typeof process.versions&&"string"==typeof process.versions.node,U="";if(Ua){var Va=require("fs"),pa=require("path");U=fa?pa.dirname(U)+"/":__dirname+"/";var Wa=function(e,b){e=e.startsWith("file://")?new URL(e):pa.normalize(e);return Va.readFileSync(e,b?void 0:"utf8")};var ma=function(e){e=Wa(e,!0);e.buffer||(e=new Uint8Array(e));return e};var na=function(e,
b,c){e=e.startsWith("file://")?new URL(e):pa.normalize(e);Va.readFile(e,function(d,g){d?c(d):b(g.buffer)})};1<process.argv.length&&process.argv[1].replace(/\\/g,"/");process.argv.slice(2);a.inspect=function(){return"[Emscripten Module object]"}}else if(ta||fa)fa?U=self.location.href:"undefined"!=typeof document&&document.currentScript&&(U=document.currentScript.src),h&&(U=h),U=0!==U.indexOf("blob:")?U.substr(0,U.replace(/[?#].*/,"").lastIndexOf("/")+1):"",Wa=function(e){var b=new XMLHttpRequest;b.open("GET",
e,!1);b.send(null);return b.responseText},fa&&(ma=function(e){var b=new XMLHttpRequest;b.open("GET",e,!1);b.responseType="arraybuffer";b.send(null);return new Uint8Array(b.response)}),na=function(e,b,c){var d=new XMLHttpRequest;d.open("GET",e,!0);d.responseType="arraybuffer";d.onload=function(){200==d.status||0==d.status&&d.response?b(d.response):c()};d.onerror=c;d.send(null)};a.print||console.log.bind(console);var da=a.printErr||console.warn.bind(console);Object.assign(a,Ta);Ta=null;var ea;a.wasmBinary&&
(ea=a.wasmBinary);"object"!=typeof WebAssembly&&y("no native wasm support detected");var ja,sa=!1,ra="undefined"!=typeof TextDecoder?new TextDecoder("utf8"):void 0,W,ia,ca,Y,ya=[],oa=[],xa=[],va=!1,ba=0,qa=null,ha=null;var P="draco_decoder_gltf.wasm";P.startsWith("data:application/octet-stream;base64,")||(P=k(P));var pd=0,qd={b:function(e,b,c){(new A(e)).init(b,c);pd++;throw e;},a:function(){y("")},d:function(e,b,c){ia.copyWithin(e,b,b+c)},c:function(e){var b=ia.length;e>>>=0;if(2147483648<e)return!1;
for(var c=1;4>=c;c*=2){var d=b*(1+.2/c);d=Math.min(d,e+100663296);var g=Math;d=Math.max(e,d);g=g.min.call(g,2147483648,d+(65536-d%65536)%65536);a:{d=ja.buffer;try{ja.grow(g-d.byteLength+65535>>>16);l();var t=1;break a}catch(aa){}t=void 0}if(t)return!0}return!1}};(function(){function e(g,t){a.asm=g.exports;ja=a.asm.e;l();oa.unshift(a.asm.f);ba--;a.monitorRunDependencies&&a.monitorRunDependencies(ba);0==ba&&(null!==qa&&(clearInterval(qa),qa=null),ha&&(g=ha,ha=null,g()))}function b(g){e(g.instance)}
function c(g){return q().then(function(t){return WebAssembly.instantiate(t,d)}).then(function(t){return t}).then(g,function(t){da("failed to asynchronously prepare wasm: "+t);y(t)})}var d={a:qd};ba++;a.monitorRunDependencies&&a.monitorRunDependencies(ba);if(a.instantiateWasm)try{return a.instantiateWasm(d,e)}catch(g){da("Module.instantiateWasm callback failed with error: "+g),ka(g)}(function(){return ea||"function"!=typeof WebAssembly.instantiateStreaming||P.startsWith("data:application/octet-stream;base64,")||
P.startsWith("file://")||Ua||"function"!=typeof fetch?c(b):fetch(P,{credentials:"same-origin"}).then(function(g){return WebAssembly.instantiateStreaming(g,d).then(b,function(t){da("wasm streaming compile failed: "+t);da("falling back to ArrayBuffer instantiation");return c(b)})})})().catch(ka);return{}})();var Xa=a._emscripten_bind_VoidPtr___destroy___0=function(){return(Xa=a._emscripten_bind_VoidPtr___destroy___0=a.asm.h).apply(null,arguments)},za=a._emscripten_bind_DecoderBuffer_DecoderBuffer_0=
function(){return(za=a._emscripten_bind_DecoderBuffer_DecoderBuffer_0=a.asm.i).apply(null,arguments)},Ya=a._emscripten_bind_DecoderBuffer_Init_2=function(){return(Ya=a._emscripten_bind_DecoderBuffer_Init_2=a.asm.j).apply(null,arguments)},Za=a._emscripten_bind_DecoderBuffer___destroy___0=function(){return(Za=a._emscripten_bind_DecoderBuffer___destroy___0=a.asm.k).apply(null,arguments)},Aa=a._emscripten_bind_AttributeTransformData_AttributeTransformData_0=function(){return(Aa=a._emscripten_bind_AttributeTransformData_AttributeTransformData_0=
a.asm.l).apply(null,arguments)},$a=a._emscripten_bind_AttributeTransformData_transform_type_0=function(){return($a=a._emscripten_bind_AttributeTransformData_transform_type_0=a.asm.m).apply(null,arguments)},ab=a._emscripten_bind_AttributeTransformData___destroy___0=function(){return(ab=a._emscripten_bind_AttributeTransformData___destroy___0=a.asm.n).apply(null,arguments)},Ba=a._emscripten_bind_GeometryAttribute_GeometryAttribute_0=function(){return(Ba=a._emscripten_bind_GeometryAttribute_GeometryAttribute_0=
a.asm.o).apply(null,arguments)},bb=a._emscripten_bind_GeometryAttribute___destroy___0=function(){return(bb=a._emscripten_bind_GeometryAttribute___destroy___0=a.asm.p).apply(null,arguments)},Ca=a._emscripten_bind_PointAttribute_PointAttribute_0=function(){return(Ca=a._emscripten_bind_PointAttribute_PointAttribute_0=a.asm.q).apply(null,arguments)},cb=a._emscripten_bind_PointAttribute_size_0=function(){return(cb=a._emscripten_bind_PointAttribute_size_0=a.asm.r).apply(null,arguments)},db=a._emscripten_bind_PointAttribute_GetAttributeTransformData_0=
function(){return(db=a._emscripten_bind_PointAttribute_GetAttributeTransformData_0=a.asm.s).apply(null,arguments)},eb=a._emscripten_bind_PointAttribute_attribute_type_0=function(){return(eb=a._emscripten_bind_PointAttribute_attribute_type_0=a.asm.t).apply(null,arguments)},fb=a._emscripten_bind_PointAttribute_data_type_0=function(){return(fb=a._emscripten_bind_PointAttribute_data_type_0=a.asm.u).apply(null,arguments)},gb=a._emscripten_bind_PointAttribute_num_components_0=function(){return(gb=a._emscripten_bind_PointAttribute_num_components_0=
a.asm.v).apply(null,arguments)},hb=a._emscripten_bind_PointAttribute_normalized_0=function(){return(hb=a._emscripten_bind_PointAttribute_normalized_0=a.asm.w).apply(null,arguments)},ib=a._emscripten_bind_PointAttribute_byte_stride_0=function(){return(ib=a._emscripten_bind_PointAttribute_byte_stride_0=a.asm.x).apply(null,arguments)},jb=a._emscripten_bind_PointAttribute_byte_offset_0=function(){return(jb=a._emscripten_bind_PointAttribute_byte_offset_0=a.asm.y).apply(null,arguments)},kb=a._emscripten_bind_PointAttribute_unique_id_0=
function(){return(kb=a._emscripten_bind_PointAttribute_unique_id_0=a.asm.z).apply(null,arguments)},lb=a._emscripten_bind_PointAttribute___destroy___0=function(){return(lb=a._emscripten_bind_PointAttribute___destroy___0=a.asm.A).apply(null,arguments)},Da=a._emscripten_bind_AttributeQuantizationTransform_AttributeQuantizationTransform_0=function(){return(Da=a._emscripten_bind_AttributeQuantizationTransform_AttributeQuantizationTransform_0=a.asm.B).apply(null,arguments)},mb=a._emscripten_bind_AttributeQuantizationTransform_InitFromAttribute_1=
function(){return(mb=a._emscripten_bind_AttributeQuantizationTransform_InitFromAttribute_1=a.asm.C).apply(null,arguments)},nb=a._emscripten_bind_AttributeQuantizationTransform_quantization_bits_0=function(){return(nb=a._emscripten_bind_AttributeQuantizationTransform_quantization_bits_0=a.asm.D).apply(null,arguments)},ob=a._emscripten_bind_AttributeQuantizationTransform_min_value_1=function(){return(ob=a._emscripten_bind_AttributeQuantizationTransform_min_value_1=a.asm.E).apply(null,arguments)},pb=
a._emscripten_bind_AttributeQuantizationTransform_range_0=function(){return(pb=a._emscripten_bind_AttributeQuantizationTransform_range_0=a.asm.F).apply(null,arguments)},qb=a._emscripten_bind_AttributeQuantizationTransform___destroy___0=function(){return(qb=a._emscripten_bind_AttributeQuantizationTransform___destroy___0=a.asm.G).apply(null,arguments)},Ea=a._emscripten_bind_AttributeOctahedronTransform_AttributeOctahedronTransform_0=function(){return(Ea=a._emscripten_bind_AttributeOctahedronTransform_AttributeOctahedronTransform_0=
a.asm.H).apply(null,arguments)},rb=a._emscripten_bind_AttributeOctahedronTransform_InitFromAttribute_1=function(){return(rb=a._emscripten_bind_AttributeOctahedronTransform_InitFromAttribute_1=a.asm.I).apply(null,arguments)},sb=a._emscripten_bind_AttributeOctahedronTransform_quantization_bits_0=function(){return(sb=a._emscripten_bind_AttributeOctahedronTransform_quantization_bits_0=a.asm.J).apply(null,arguments)},tb=a._emscripten_bind_AttributeOctahedronTransform___destroy___0=function(){return(tb=
a._emscripten_bind_AttributeOctahedronTransform___destroy___0=a.asm.K).apply(null,arguments)},Fa=a._emscripten_bind_PointCloud_PointCloud_0=function(){return(Fa=a._emscripten_bind_PointCloud_PointCloud_0=a.asm.L).apply(null,arguments)},ub=a._emscripten_bind_PointCloud_num_attributes_0=function(){return(ub=a._emscripten_bind_PointCloud_num_attributes_0=a.asm.M).apply(null,arguments)},vb=a._emscripten_bind_PointCloud_num_points_0=function(){return(vb=a._emscripten_bind_PointCloud_num_points_0=a.asm.N).apply(null,
arguments)},wb=a._emscripten_bind_PointCloud___destroy___0=function(){return(wb=a._emscripten_bind_PointCloud___destroy___0=a.asm.O).apply(null,arguments)},Ga=a._emscripten_bind_Mesh_Mesh_0=function(){return(Ga=a._emscripten_bind_Mesh_Mesh_0=a.asm.P).apply(null,arguments)},xb=a._emscripten_bind_Mesh_num_faces_0=function(){return(xb=a._emscripten_bind_Mesh_num_faces_0=a.asm.Q).apply(null,arguments)},yb=a._emscripten_bind_Mesh_num_attributes_0=function(){return(yb=a._emscripten_bind_Mesh_num_attributes_0=
a.asm.R).apply(null,arguments)},zb=a._emscripten_bind_Mesh_num_points_0=function(){return(zb=a._emscripten_bind_Mesh_num_points_0=a.asm.S).apply(null,arguments)},Ab=a._emscripten_bind_Mesh___destroy___0=function(){return(Ab=a._emscripten_bind_Mesh___destroy___0=a.asm.T).apply(null,arguments)},Ha=a._emscripten_bind_Metadata_Metadata_0=function(){return(Ha=a._emscripten_bind_Metadata_Metadata_0=a.asm.U).apply(null,arguments)},Bb=a._emscripten_bind_Metadata___destroy___0=function(){return(Bb=a._emscripten_bind_Metadata___destroy___0=
a.asm.V).apply(null,arguments)},Cb=a._emscripten_bind_Status_code_0=function(){return(Cb=a._emscripten_bind_Status_code_0=a.asm.W).apply(null,arguments)},Db=a._emscripten_bind_Status_ok_0=function(){return(Db=a._emscripten_bind_Status_ok_0=a.asm.X).apply(null,arguments)},Eb=a._emscripten_bind_Status_error_msg_0=function(){return(Eb=a._emscripten_bind_Status_error_msg_0=a.asm.Y).apply(null,arguments)},Fb=a._emscripten_bind_Status___destroy___0=function(){return(Fb=a._emscripten_bind_Status___destroy___0=
a.asm.Z).apply(null,arguments)},Ia=a._emscripten_bind_DracoFloat32Array_DracoFloat32Array_0=function(){return(Ia=a._emscripten_bind_DracoFloat32Array_DracoFloat32Array_0=a.asm._).apply(null,arguments)},Gb=a._emscripten_bind_DracoFloat32Array_GetValue_1=function(){return(Gb=a._emscripten_bind_DracoFloat32Array_GetValue_1=a.asm.$).apply(null,arguments)},Hb=a._emscripten_bind_DracoFloat32Array_size_0=function(){return(Hb=a._emscripten_bind_DracoFloat32Array_size_0=a.asm.aa).apply(null,arguments)},Ib=
a._emscripten_bind_DracoFloat32Array___destroy___0=function(){return(Ib=a._emscripten_bind_DracoFloat32Array___destroy___0=a.asm.ba).apply(null,arguments)},Ja=a._emscripten_bind_DracoInt8Array_DracoInt8Array_0=function(){return(Ja=a._emscripten_bind_DracoInt8Array_DracoInt8Array_0=a.asm.ca).apply(null,arguments)},Jb=a._emscripten_bind_DracoInt8Array_GetValue_1=function(){return(Jb=a._emscripten_bind_DracoInt8Array_GetValue_1=a.asm.da).apply(null,arguments)},Kb=a._emscripten_bind_DracoInt8Array_size_0=
function(){return(Kb=a._emscripten_bind_DracoInt8Array_size_0=a.asm.ea).apply(null,arguments)},Lb=a._emscripten_bind_DracoInt8Array___destroy___0=function(){return(Lb=a._emscripten_bind_DracoInt8Array___destroy___0=a.asm.fa).apply(null,arguments)},Ka=a._emscripten_bind_DracoUInt8Array_DracoUInt8Array_0=function(){return(Ka=a._emscripten_bind_DracoUInt8Array_DracoUInt8Array_0=a.asm.ga).apply(null,arguments)},Mb=a._emscripten_bind_DracoUInt8Array_GetValue_1=function(){return(Mb=a._emscripten_bind_DracoUInt8Array_GetValue_1=
a.asm.ha).apply(null,arguments)},Nb=a._emscripten_bind_DracoUInt8Array_size_0=function(){return(Nb=a._emscripten_bind_DracoUInt8Array_size_0=a.asm.ia).apply(null,arguments)},Ob=a._emscripten_bind_DracoUInt8Array___destroy___0=function(){return(Ob=a._emscripten_bind_DracoUInt8Array___destroy___0=a.asm.ja).apply(null,arguments)},La=a._emscripten_bind_DracoInt16Array_DracoInt16Array_0=function(){return(La=a._emscripten_bind_DracoInt16Array_DracoInt16Array_0=a.asm.ka).apply(null,arguments)},Pb=a._emscripten_bind_DracoInt16Array_GetValue_1=
function(){return(Pb=a._emscripten_bind_DracoInt16Array_GetValue_1=a.asm.la).apply(null,arguments)},Qb=a._emscripten_bind_DracoInt16Array_size_0=function(){return(Qb=a._emscripten_bind_DracoInt16Array_size_0=a.asm.ma).apply(null,arguments)},Rb=a._emscripten_bind_DracoInt16Array___destroy___0=function(){return(Rb=a._emscripten_bind_DracoInt16Array___destroy___0=a.asm.na).apply(null,arguments)},Ma=a._emscripten_bind_DracoUInt16Array_DracoUInt16Array_0=function(){return(Ma=a._emscripten_bind_DracoUInt16Array_DracoUInt16Array_0=
a.asm.oa).apply(null,arguments)},Sb=a._emscripten_bind_DracoUInt16Array_GetValue_1=function(){return(Sb=a._emscripten_bind_DracoUInt16Array_GetValue_1=a.asm.pa).apply(null,arguments)},Tb=a._emscripten_bind_DracoUInt16Array_size_0=function(){return(Tb=a._emscripten_bind_DracoUInt16Array_size_0=a.asm.qa).apply(null,arguments)},Ub=a._emscripten_bind_DracoUInt16Array___destroy___0=function(){return(Ub=a._emscripten_bind_DracoUInt16Array___destroy___0=a.asm.ra).apply(null,arguments)},Na=a._emscripten_bind_DracoInt32Array_DracoInt32Array_0=
function(){return(Na=a._emscripten_bind_DracoInt32Array_DracoInt32Array_0=a.asm.sa).apply(null,arguments)},Vb=a._emscripten_bind_DracoInt32Array_GetValue_1=function(){return(Vb=a._emscripten_bind_DracoInt32Array_GetValue_1=a.asm.ta).apply(null,arguments)},Wb=a._emscripten_bind_DracoInt32Array_size_0=function(){return(Wb=a._emscripten_bind_DracoInt32Array_size_0=a.asm.ua).apply(null,arguments)},Xb=a._emscripten_bind_DracoInt32Array___destroy___0=function(){return(Xb=a._emscripten_bind_DracoInt32Array___destroy___0=
a.asm.va).apply(null,arguments)},Oa=a._emscripten_bind_DracoUInt32Array_DracoUInt32Array_0=function(){return(Oa=a._emscripten_bind_DracoUInt32Array_DracoUInt32Array_0=a.asm.wa).apply(null,arguments)},Yb=a._emscripten_bind_DracoUInt32Array_GetValue_1=function(){return(Yb=a._emscripten_bind_DracoUInt32Array_GetValue_1=a.asm.xa).apply(null,arguments)},Zb=a._emscripten_bind_DracoUInt32Array_size_0=function(){return(Zb=a._emscripten_bind_DracoUInt32Array_size_0=a.asm.ya).apply(null,arguments)},$b=a._emscripten_bind_DracoUInt32Array___destroy___0=
function(){return($b=a._emscripten_bind_DracoUInt32Array___destroy___0=a.asm.za).apply(null,arguments)},Pa=a._emscripten_bind_MetadataQuerier_MetadataQuerier_0=function(){return(Pa=a._emscripten_bind_MetadataQuerier_MetadataQuerier_0=a.asm.Aa).apply(null,arguments)},ac=a._emscripten_bind_MetadataQuerier_HasEntry_2=function(){return(ac=a._emscripten_bind_MetadataQuerier_HasEntry_2=a.asm.Ba).apply(null,arguments)},bc=a._emscripten_bind_MetadataQuerier_GetIntEntry_2=function(){return(bc=a._emscripten_bind_MetadataQuerier_GetIntEntry_2=
a.asm.Ca).apply(null,arguments)},cc=a._emscripten_bind_MetadataQuerier_GetIntEntryArray_3=function(){return(cc=a._emscripten_bind_MetadataQuerier_GetIntEntryArray_3=a.asm.Da).apply(null,arguments)},dc=a._emscripten_bind_MetadataQuerier_GetDoubleEntry_2=function(){return(dc=a._emscripten_bind_MetadataQuerier_GetDoubleEntry_2=a.asm.Ea).apply(null,arguments)},ec=a._emscripten_bind_MetadataQuerier_GetStringEntry_2=function(){return(ec=a._emscripten_bind_MetadataQuerier_GetStringEntry_2=a.asm.Fa).apply(null,
arguments)},fc=a._emscripten_bind_MetadataQuerier_NumEntries_1=function(){return(fc=a._emscripten_bind_MetadataQuerier_NumEntries_1=a.asm.Ga).apply(null,arguments)},gc=a._emscripten_bind_MetadataQuerier_GetEntryName_2=function(){return(gc=a._emscripten_bind_MetadataQuerier_GetEntryName_2=a.asm.Ha).apply(null,arguments)},hc=a._emscripten_bind_MetadataQuerier___destroy___0=function(){return(hc=a._emscripten_bind_MetadataQuerier___destroy___0=a.asm.Ia).apply(null,arguments)},Qa=a._emscripten_bind_Decoder_Decoder_0=
function(){return(Qa=a._emscripten_bind_Decoder_Decoder_0=a.asm.Ja).apply(null,arguments)},ic=a._emscripten_bind_Decoder_DecodeArrayToPointCloud_3=function(){return(ic=a._emscripten_bind_Decoder_DecodeArrayToPointCloud_3=a.asm.Ka).apply(null,arguments)},jc=a._emscripten_bind_Decoder_DecodeArrayToMesh_3=function(){return(jc=a._emscripten_bind_Decoder_DecodeArrayToMesh_3=a.asm.La).apply(null,arguments)},kc=a._emscripten_bind_Decoder_GetAttributeId_2=function(){return(kc=a._emscripten_bind_Decoder_GetAttributeId_2=
a.asm.Ma).apply(null,arguments)},lc=a._emscripten_bind_Decoder_GetAttributeIdByName_2=function(){return(lc=a._emscripten_bind_Decoder_GetAttributeIdByName_2=a.asm.Na).apply(null,arguments)},mc=a._emscripten_bind_Decoder_GetAttributeIdByMetadataEntry_3=function(){return(mc=a._emscripten_bind_Decoder_GetAttributeIdByMetadataEntry_3=a.asm.Oa).apply(null,arguments)},nc=a._emscripten_bind_Decoder_GetAttribute_2=function(){return(nc=a._emscripten_bind_Decoder_GetAttribute_2=a.asm.Pa).apply(null,arguments)},
oc=a._emscripten_bind_Decoder_GetAttributeByUniqueId_2=function(){return(oc=a._emscripten_bind_Decoder_GetAttributeByUniqueId_2=a.asm.Qa).apply(null,arguments)},pc=a._emscripten_bind_Decoder_GetMetadata_1=function(){return(pc=a._emscripten_bind_Decoder_GetMetadata_1=a.asm.Ra).apply(null,arguments)},qc=a._emscripten_bind_Decoder_GetAttributeMetadata_2=function(){return(qc=a._emscripten_bind_Decoder_GetAttributeMetadata_2=a.asm.Sa).apply(null,arguments)},rc=a._emscripten_bind_Decoder_GetFaceFromMesh_3=
function(){return(rc=a._emscripten_bind_Decoder_GetFaceFromMesh_3=a.asm.Ta).apply(null,arguments)},sc=a._emscripten_bind_Decoder_GetTriangleStripsFromMesh_2=function(){return(sc=a._emscripten_bind_Decoder_GetTriangleStripsFromMesh_2=a.asm.Ua).apply(null,arguments)},tc=a._emscripten_bind_Decoder_GetTrianglesUInt16Array_3=function(){return(tc=a._emscripten_bind_Decoder_GetTrianglesUInt16Array_3=a.asm.Va).apply(null,arguments)},uc=a._emscripten_bind_Decoder_GetTrianglesUInt32Array_3=function(){return(uc=
a._emscripten_bind_Decoder_GetTrianglesUInt32Array_3=a.asm.Wa).apply(null,arguments)},vc=a._emscripten_bind_Decoder_GetAttributeFloat_3=function(){return(vc=a._emscripten_bind_Decoder_GetAttributeFloat_3=a.asm.Xa).apply(null,arguments)},wc=a._emscripten_bind_Decoder_GetAttributeFloatForAllPoints_3=function(){return(wc=a._emscripten_bind_Decoder_GetAttributeFloatForAllPoints_3=a.asm.Ya).apply(null,arguments)},xc=a._emscripten_bind_Decoder_GetAttributeIntForAllPoints_3=function(){return(xc=a._emscripten_bind_Decoder_GetAttributeIntForAllPoints_3=
a.asm.Za).apply(null,arguments)},yc=a._emscripten_bind_Decoder_GetAttributeInt8ForAllPoints_3=function(){return(yc=a._emscripten_bind_Decoder_GetAttributeInt8ForAllPoints_3=a.asm._a).apply(null,arguments)},zc=a._emscripten_bind_Decoder_GetAttributeUInt8ForAllPoints_3=function(){return(zc=a._emscripten_bind_Decoder_GetAttributeUInt8ForAllPoints_3=a.asm.$a).apply(null,arguments)},Ac=a._emscripten_bind_Decoder_GetAttributeInt16ForAllPoints_3=function(){return(Ac=a._emscripten_bind_Decoder_GetAttributeInt16ForAllPoints_3=
a.asm.ab).apply(null,arguments)},Bc=a._emscripten_bind_Decoder_GetAttributeUInt16ForAllPoints_3=function(){return(Bc=a._emscripten_bind_Decoder_GetAttributeUInt16ForAllPoints_3=a.asm.bb).apply(null,arguments)},Cc=a._emscripten_bind_Decoder_GetAttributeInt32ForAllPoints_3=function(){return(Cc=a._emscripten_bind_Decoder_GetAttributeInt32ForAllPoints_3=a.asm.cb).apply(null,arguments)},Dc=a._emscripten_bind_Decoder_GetAttributeUInt32ForAllPoints_3=function(){return(Dc=a._emscripten_bind_Decoder_GetAttributeUInt32ForAllPoints_3=
a.asm.db).apply(null,arguments)},Ec=a._emscripten_bind_Decoder_GetAttributeDataArrayForAllPoints_5=function(){return(Ec=a._emscripten_bind_Decoder_GetAttributeDataArrayForAllPoints_5=a.asm.eb).apply(null,arguments)},Fc=a._emscripten_bind_Decoder_SkipAttributeTransform_1=function(){return(Fc=a._emscripten_bind_Decoder_SkipAttributeTransform_1=a.asm.fb).apply(null,arguments)},Gc=a._emscripten_bind_Decoder_GetEncodedGeometryType_Deprecated_1=function(){return(Gc=a._emscripten_bind_Decoder_GetEncodedGeometryType_Deprecated_1=
a.asm.gb).apply(null,arguments)},Hc=a._emscripten_bind_Decoder_DecodeBufferToPointCloud_2=function(){return(Hc=a._emscripten_bind_Decoder_DecodeBufferToPointCloud_2=a.asm.hb).apply(null,arguments)},Ic=a._emscripten_bind_Decoder_DecodeBufferToMesh_2=function(){return(Ic=a._emscripten_bind_Decoder_DecodeBufferToMesh_2=a.asm.ib).apply(null,arguments)},Jc=a._emscripten_bind_Decoder___destroy___0=function(){return(Jc=a._emscripten_bind_Decoder___destroy___0=a.asm.jb).apply(null,arguments)},Kc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_INVALID_TRANSFORM=
function(){return(Kc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_INVALID_TRANSFORM=a.asm.kb).apply(null,arguments)},Lc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_NO_TRANSFORM=function(){return(Lc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_NO_TRANSFORM=a.asm.lb).apply(null,arguments)},Mc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_QUANTIZATION_TRANSFORM=function(){return(Mc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_QUANTIZATION_TRANSFORM=
a.asm.mb).apply(null,arguments)},Nc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_OCTAHEDRON_TRANSFORM=function(){return(Nc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_OCTAHEDRON_TRANSFORM=a.asm.nb).apply(null,arguments)},Oc=a._emscripten_enum_draco_GeometryAttribute_Type_INVALID=function(){return(Oc=a._emscripten_enum_draco_GeometryAttribute_Type_INVALID=a.asm.ob).apply(null,arguments)},Pc=a._emscripten_enum_draco_GeometryAttribute_Type_POSITION=function(){return(Pc=a._emscripten_enum_draco_GeometryAttribute_Type_POSITION=
a.asm.pb).apply(null,arguments)},Qc=a._emscripten_enum_draco_GeometryAttribute_Type_NORMAL=function(){return(Qc=a._emscripten_enum_draco_GeometryAttribute_Type_NORMAL=a.asm.qb).apply(null,arguments)},Rc=a._emscripten_enum_draco_GeometryAttribute_Type_COLOR=function(){return(Rc=a._emscripten_enum_draco_GeometryAttribute_Type_COLOR=a.asm.rb).apply(null,arguments)},Sc=a._emscripten_enum_draco_GeometryAttribute_Type_TEX_COORD=function(){return(Sc=a._emscripten_enum_draco_GeometryAttribute_Type_TEX_COORD=
a.asm.sb).apply(null,arguments)},Tc=a._emscripten_enum_draco_GeometryAttribute_Type_GENERIC=function(){return(Tc=a._emscripten_enum_draco_GeometryAttribute_Type_GENERIC=a.asm.tb).apply(null,arguments)},Uc=a._emscripten_enum_draco_EncodedGeometryType_INVALID_GEOMETRY_TYPE=function(){return(Uc=a._emscripten_enum_draco_EncodedGeometryType_INVALID_GEOMETRY_TYPE=a.asm.ub).apply(null,arguments)},Vc=a._emscripten_enum_draco_EncodedGeometryType_POINT_CLOUD=function(){return(Vc=a._emscripten_enum_draco_EncodedGeometryType_POINT_CLOUD=
a.asm.vb).apply(null,arguments)},Wc=a._emscripten_enum_draco_EncodedGeometryType_TRIANGULAR_MESH=function(){return(Wc=a._emscripten_enum_draco_EncodedGeometryType_TRIANGULAR_MESH=a.asm.wb).apply(null,arguments)},Xc=a._emscripten_enum_draco_DataType_DT_INVALID=function(){return(Xc=a._emscripten_enum_draco_DataType_DT_INVALID=a.asm.xb).apply(null,arguments)},Yc=a._emscripten_enum_draco_DataType_DT_INT8=function(){return(Yc=a._emscripten_enum_draco_DataType_DT_INT8=a.asm.yb).apply(null,arguments)},Zc=
a._emscripten_enum_draco_DataType_DT_UINT8=function(){return(Zc=a._emscripten_enum_draco_DataType_DT_UINT8=a.asm.zb).apply(null,arguments)},$c=a._emscripten_enum_draco_DataType_DT_INT16=function(){return($c=a._emscripten_enum_draco_DataType_DT_INT16=a.asm.Ab).apply(null,arguments)},ad=a._emscripten_enum_draco_DataType_DT_UINT16=function(){return(ad=a._emscripten_enum_draco_DataType_DT_UINT16=a.asm.Bb).apply(null,arguments)},bd=a._emscripten_enum_draco_DataType_DT_INT32=function(){return(bd=a._emscripten_enum_draco_DataType_DT_INT32=
a.asm.Cb).apply(null,arguments)},cd=a._emscripten_enum_draco_DataType_DT_UINT32=function(){return(cd=a._emscripten_enum_draco_DataType_DT_UINT32=a.asm.Db).apply(null,arguments)},dd=a._emscripten_enum_draco_DataType_DT_INT64=function(){return(dd=a._emscripten_enum_draco_DataType_DT_INT64=a.asm.Eb).apply(null,arguments)},ed=a._emscripten_enum_draco_DataType_DT_UINT64=function(){return(ed=a._emscripten_enum_draco_DataType_DT_UINT64=a.asm.Fb).apply(null,arguments)},fd=a._emscripten_enum_draco_DataType_DT_FLOAT32=
function(){return(fd=a._emscripten_enum_draco_DataType_DT_FLOAT32=a.asm.Gb).apply(null,arguments)},gd=a._emscripten_enum_draco_DataType_DT_FLOAT64=function(){return(gd=a._emscripten_enum_draco_DataType_DT_FLOAT64=a.asm.Hb).apply(null,arguments)},hd=a._emscripten_enum_draco_DataType_DT_BOOL=function(){return(hd=a._emscripten_enum_draco_DataType_DT_BOOL=a.asm.Ib).apply(null,arguments)},id=a._emscripten_enum_draco_DataType_DT_TYPES_COUNT=function(){return(id=a._emscripten_enum_draco_DataType_DT_TYPES_COUNT=
a.asm.Jb).apply(null,arguments)},jd=a._emscripten_enum_draco_StatusCode_OK=function(){return(jd=a._emscripten_enum_draco_StatusCode_OK=a.asm.Kb).apply(null,arguments)},kd=a._emscripten_enum_draco_StatusCode_DRACO_ERROR=function(){return(kd=a._emscripten_enum_draco_StatusCode_DRACO_ERROR=a.asm.Lb).apply(null,arguments)},ld=a._emscripten_enum_draco_StatusCode_IO_ERROR=function(){return(ld=a._emscripten_enum_draco_StatusCode_IO_ERROR=a.asm.Mb).apply(null,arguments)},md=a._emscripten_enum_draco_StatusCode_INVALID_PARAMETER=
function(){return(md=a._emscripten_enum_draco_StatusCode_INVALID_PARAMETER=a.asm.Nb).apply(null,arguments)},nd=a._emscripten_enum_draco_StatusCode_UNSUPPORTED_VERSION=function(){return(nd=a._emscripten_enum_draco_StatusCode_UNSUPPORTED_VERSION=a.asm.Ob).apply(null,arguments)},od=a._emscripten_enum_draco_StatusCode_UNKNOWN_VERSION=function(){return(od=a._emscripten_enum_draco_StatusCode_UNKNOWN_VERSION=a.asm.Pb).apply(null,arguments)};a._malloc=function(){return(a._malloc=a.asm.Qb).apply(null,arguments)};
a._free=function(){return(a._free=a.asm.Rb).apply(null,arguments)};var ua=function(){return(ua=a.asm.Sb).apply(null,arguments)};a.___start_em_js=11660;a.___stop_em_js=11758;var la;ha=function b(){la||F();la||(ha=b)};if(a.preInit)for("function"==typeof a.preInit&&(a.preInit=[a.preInit]);0<a.preInit.length;)a.preInit.pop()();F();v.prototype=Object.create(v.prototype);v.prototype.constructor=v;v.prototype.__class__=v;v.__cache__={};a.WrapperObject=v;a.getCache=w;a.wrapPointer=B;a.castObject=function(b,
c){return B(b.ptr,c)};a.NULL=B(0);a.destroy=function(b){if(!b.__destroy__)throw"Error: Cannot destroy object. (Did you create it yourself?)";b.__destroy__();delete w(b.__class__)[b.ptr]};a.compare=function(b,c){return b.ptr===c.ptr};a.getPointer=function(b){return b.ptr};a.getClass=function(b){return b.__class__};var r={buffer:0,size:0,pos:0,temps:[],needed:0,prepare:function(){if(r.needed){for(var b=0;b<r.temps.length;b++)a._free(r.temps[b]);r.temps.length=0;a._free(r.buffer);r.buffer=0;r.size+=
r.needed;r.needed=0}r.buffer||(r.size+=128,r.buffer=a._malloc(r.size),r.buffer||y(void 0));r.pos=0},alloc:function(b,c){r.buffer||y(void 0);b=b.length*c.BYTES_PER_ELEMENT;b=b+7&-8;r.pos+b>=r.size?(0<b||y(void 0),r.needed+=b,c=a._malloc(b),r.temps.push(c)):(c=r.buffer+r.pos,r.pos+=b);return c},copy:function(b,c,d){d>>>=0;switch(c.BYTES_PER_ELEMENT){case 2:d>>>=1;break;case 4:d>>>=2;break;case 8:d>>>=3}for(var g=0;g<b.length;g++)c[d+g]=b[g]}};X.prototype=Object.create(v.prototype);X.prototype.constructor=
X;X.prototype.__class__=X;X.__cache__={};a.VoidPtr=X;X.prototype.__destroy__=X.prototype.__destroy__=function(){Xa(this.ptr)};S.prototype=Object.create(v.prototype);S.prototype.constructor=S;S.prototype.__class__=S;S.__cache__={};a.DecoderBuffer=S;S.prototype.Init=S.prototype.Init=function(b,c){var d=this.ptr;r.prepare();"object"==typeof b&&(b=Z(b));c&&"object"===typeof c&&(c=c.ptr);Ya(d,b,c)};S.prototype.__destroy__=S.prototype.__destroy__=function(){Za(this.ptr)};Q.prototype=Object.create(v.prototype);
Q.prototype.constructor=Q;Q.prototype.__class__=Q;Q.__cache__={};a.AttributeTransformData=Q;Q.prototype.transform_type=Q.prototype.transform_type=function(){return $a(this.ptr)};Q.prototype.__destroy__=Q.prototype.__destroy__=function(){ab(this.ptr)};V.prototype=Object.create(v.prototype);V.prototype.constructor=V;V.prototype.__class__=V;V.__cache__={};a.GeometryAttribute=V;V.prototype.__destroy__=V.prototype.__destroy__=function(){bb(this.ptr)};x.prototype=Object.create(v.prototype);x.prototype.constructor=
x;x.prototype.__class__=x;x.__cache__={};a.PointAttribute=x;x.prototype.size=x.prototype.size=function(){return cb(this.ptr)};x.prototype.GetAttributeTransformData=x.prototype.GetAttributeTransformData=function(){return B(db(this.ptr),Q)};x.prototype.attribute_type=x.prototype.attribute_type=function(){return eb(this.ptr)};x.prototype.data_type=x.prototype.data_type=function(){return fb(this.ptr)};x.prototype.num_components=x.prototype.num_components=function(){return gb(this.ptr)};x.prototype.normalized=
x.prototype.normalized=function(){return!!hb(this.ptr)};x.prototype.byte_stride=x.prototype.byte_stride=function(){return ib(this.ptr)};x.prototype.byte_offset=x.prototype.byte_offset=function(){return jb(this.ptr)};x.prototype.unique_id=x.prototype.unique_id=function(){return kb(this.ptr)};x.prototype.__destroy__=x.prototype.__destroy__=function(){lb(this.ptr)};D.prototype=Object.create(v.prototype);D.prototype.constructor=D;D.prototype.__class__=D;D.__cache__={};a.AttributeQuantizationTransform=
D;D.prototype.InitFromAttribute=D.prototype.InitFromAttribute=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return!!mb(c,b)};D.prototype.quantization_bits=D.prototype.quantization_bits=function(){return nb(this.ptr)};D.prototype.min_value=D.prototype.min_value=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return ob(c,b)};D.prototype.range=D.prototype.range=function(){return pb(this.ptr)};D.prototype.__destroy__=D.prototype.__destroy__=function(){qb(this.ptr)};G.prototype=
Object.create(v.prototype);G.prototype.constructor=G;G.prototype.__class__=G;G.__cache__={};a.AttributeOctahedronTransform=G;G.prototype.InitFromAttribute=G.prototype.InitFromAttribute=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return!!rb(c,b)};G.prototype.quantization_bits=G.prototype.quantization_bits=function(){return sb(this.ptr)};G.prototype.__destroy__=G.prototype.__destroy__=function(){tb(this.ptr)};H.prototype=Object.create(v.prototype);H.prototype.constructor=H;H.prototype.__class__=
H;H.__cache__={};a.PointCloud=H;H.prototype.num_attributes=H.prototype.num_attributes=function(){return ub(this.ptr)};H.prototype.num_points=H.prototype.num_points=function(){return vb(this.ptr)};H.prototype.__destroy__=H.prototype.__destroy__=function(){wb(this.ptr)};E.prototype=Object.create(v.prototype);E.prototype.constructor=E;E.prototype.__class__=E;E.__cache__={};a.Mesh=E;E.prototype.num_faces=E.prototype.num_faces=function(){return xb(this.ptr)};E.prototype.num_attributes=E.prototype.num_attributes=
function(){return yb(this.ptr)};E.prototype.num_points=E.prototype.num_points=function(){return zb(this.ptr)};E.prototype.__destroy__=E.prototype.__destroy__=function(){Ab(this.ptr)};T.prototype=Object.create(v.prototype);T.prototype.constructor=T;T.prototype.__class__=T;T.__cache__={};a.Metadata=T;T.prototype.__destroy__=T.prototype.__destroy__=function(){Bb(this.ptr)};C.prototype=Object.create(v.prototype);C.prototype.constructor=C;C.prototype.__class__=C;C.__cache__={};a.Status=C;C.prototype.code=
C.prototype.code=function(){return Cb(this.ptr)};C.prototype.ok=C.prototype.ok=function(){return!!Db(this.ptr)};C.prototype.error_msg=C.prototype.error_msg=function(){return p(Eb(this.ptr))};C.prototype.__destroy__=C.prototype.__destroy__=function(){Fb(this.ptr)};I.prototype=Object.create(v.prototype);I.prototype.constructor=I;I.prototype.__class__=I;I.__cache__={};a.DracoFloat32Array=I;I.prototype.GetValue=I.prototype.GetValue=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return Gb(c,
b)};I.prototype.size=I.prototype.size=function(){return Hb(this.ptr)};I.prototype.__destroy__=I.prototype.__destroy__=function(){Ib(this.ptr)};J.prototype=Object.create(v.prototype);J.prototype.constructor=J;J.prototype.__class__=J;J.__cache__={};a.DracoInt8Array=J;J.prototype.GetValue=J.prototype.GetValue=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return Jb(c,b)};J.prototype.size=J.prototype.size=function(){return Kb(this.ptr)};J.prototype.__destroy__=J.prototype.__destroy__=function(){Lb(this.ptr)};
K.prototype=Object.create(v.prototype);K.prototype.constructor=K;K.prototype.__class__=K;K.__cache__={};a.DracoUInt8Array=K;K.prototype.GetValue=K.prototype.GetValue=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return Mb(c,b)};K.prototype.size=K.prototype.size=function(){return Nb(this.ptr)};K.prototype.__destroy__=K.prototype.__destroy__=function(){Ob(this.ptr)};L.prototype=Object.create(v.prototype);L.prototype.constructor=L;L.prototype.__class__=L;L.__cache__={};a.DracoInt16Array=
L;L.prototype.GetValue=L.prototype.GetValue=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return Pb(c,b)};L.prototype.size=L.prototype.size=function(){return Qb(this.ptr)};L.prototype.__destroy__=L.prototype.__destroy__=function(){Rb(this.ptr)};M.prototype=Object.create(v.prototype);M.prototype.constructor=M;M.prototype.__class__=M;M.__cache__={};a.DracoUInt16Array=M;M.prototype.GetValue=M.prototype.GetValue=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return Sb(c,b)};
M.prototype.size=M.prototype.size=function(){return Tb(this.ptr)};M.prototype.__destroy__=M.prototype.__destroy__=function(){Ub(this.ptr)};N.prototype=Object.create(v.prototype);N.prototype.constructor=N;N.prototype.__class__=N;N.__cache__={};a.DracoInt32Array=N;N.prototype.GetValue=N.prototype.GetValue=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return Vb(c,b)};N.prototype.size=N.prototype.size=function(){return Wb(this.ptr)};N.prototype.__destroy__=N.prototype.__destroy__=function(){Xb(this.ptr)};
O.prototype=Object.create(v.prototype);O.prototype.constructor=O;O.prototype.__class__=O;O.__cache__={};a.DracoUInt32Array=O;O.prototype.GetValue=O.prototype.GetValue=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return Yb(c,b)};O.prototype.size=O.prototype.size=function(){return Zb(this.ptr)};O.prototype.__destroy__=O.prototype.__destroy__=function(){$b(this.ptr)};z.prototype=Object.create(v.prototype);z.prototype.constructor=z;z.prototype.__class__=z;z.__cache__={};a.MetadataQuerier=
z;z.prototype.HasEntry=z.prototype.HasEntry=function(b,c){var d=this.ptr;r.prepare();b&&"object"===typeof b&&(b=b.ptr);c=c&&"object"===typeof c?c.ptr:R(c);return!!ac(d,b,c)};z.prototype.GetIntEntry=z.prototype.GetIntEntry=function(b,c){var d=this.ptr;r.prepare();b&&"object"===typeof b&&(b=b.ptr);c=c&&"object"===typeof c?c.ptr:R(c);return bc(d,b,c)};z.prototype.GetIntEntryArray=z.prototype.GetIntEntryArray=function(b,c,d){var g=this.ptr;r.prepare();b&&"object"===typeof b&&(b=b.ptr);c=c&&"object"===
typeof c?c.ptr:R(c);d&&"object"===typeof d&&(d=d.ptr);cc(g,b,c,d)};z.prototype.GetDoubleEntry=z.prototype.GetDoubleEntry=function(b,c){var d=this.ptr;r.prepare();b&&"object"===typeof b&&(b=b.ptr);c=c&&"object"===typeof c?c.ptr:R(c);return dc(d,b,c)};z.prototype.GetStringEntry=z.prototype.GetStringEntry=function(b,c){var d=this.ptr;r.prepare();b&&"object"===typeof b&&(b=b.ptr);c=c&&"object"===typeof c?c.ptr:R(c);return p(ec(d,b,c))};z.prototype.NumEntries=z.prototype.NumEntries=function(b){var c=this.ptr;
b&&"object"===typeof b&&(b=b.ptr);return fc(c,b)};z.prototype.GetEntryName=z.prototype.GetEntryName=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);return p(gc(d,b,c))};z.prototype.__destroy__=z.prototype.__destroy__=function(){hc(this.ptr)};m.prototype=Object.create(v.prototype);m.prototype.constructor=m;m.prototype.__class__=m;m.__cache__={};a.Decoder=m;m.prototype.DecodeArrayToPointCloud=m.prototype.DecodeArrayToPointCloud=function(b,c,d){var g=
this.ptr;r.prepare();"object"==typeof b&&(b=Z(b));c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return B(ic(g,b,c,d),C)};m.prototype.DecodeArrayToMesh=m.prototype.DecodeArrayToMesh=function(b,c,d){var g=this.ptr;r.prepare();"object"==typeof b&&(b=Z(b));c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return B(jc(g,b,c,d),C)};m.prototype.GetAttributeId=m.prototype.GetAttributeId=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&
(c=c.ptr);return kc(d,b,c)};m.prototype.GetAttributeIdByName=m.prototype.GetAttributeIdByName=function(b,c){var d=this.ptr;r.prepare();b&&"object"===typeof b&&(b=b.ptr);c=c&&"object"===typeof c?c.ptr:R(c);return lc(d,b,c)};m.prototype.GetAttributeIdByMetadataEntry=m.prototype.GetAttributeIdByMetadataEntry=function(b,c,d){var g=this.ptr;r.prepare();b&&"object"===typeof b&&(b=b.ptr);c=c&&"object"===typeof c?c.ptr:R(c);d=d&&"object"===typeof d?d.ptr:R(d);return mc(g,b,c,d)};m.prototype.GetAttribute=
m.prototype.GetAttribute=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);return B(nc(d,b,c),x)};m.prototype.GetAttributeByUniqueId=m.prototype.GetAttributeByUniqueId=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);return B(oc(d,b,c),x)};m.prototype.GetMetadata=m.prototype.GetMetadata=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return B(pc(c,b),T)};m.prototype.GetAttributeMetadata=m.prototype.GetAttributeMetadata=
function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);return B(qc(d,b,c),T)};m.prototype.GetFaceFromMesh=m.prototype.GetFaceFromMesh=function(b,c,d){var g=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!rc(g,b,c,d)};m.prototype.GetTriangleStripsFromMesh=m.prototype.GetTriangleStripsFromMesh=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);
return sc(d,b,c)};m.prototype.GetTrianglesUInt16Array=m.prototype.GetTrianglesUInt16Array=function(b,c,d){var g=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!tc(g,b,c,d)};m.prototype.GetTrianglesUInt32Array=m.prototype.GetTrianglesUInt32Array=function(b,c,d){var g=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!uc(g,b,c,d)};m.prototype.GetAttributeFloat=m.prototype.GetAttributeFloat=
function(b,c,d){var g=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!vc(g,b,c,d)};m.prototype.GetAttributeFloatForAllPoints=m.prototype.GetAttributeFloatForAllPoints=function(b,c,d){var g=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!wc(g,b,c,d)};m.prototype.GetAttributeIntForAllPoints=m.prototype.GetAttributeIntForAllPoints=function(b,c,d){var g=this.ptr;
b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!xc(g,b,c,d)};m.prototype.GetAttributeInt8ForAllPoints=m.prototype.GetAttributeInt8ForAllPoints=function(b,c,d){var g=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!yc(g,b,c,d)};m.prototype.GetAttributeUInt8ForAllPoints=m.prototype.GetAttributeUInt8ForAllPoints=function(b,c,d){var g=this.ptr;b&&"object"===typeof b&&(b=
b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!zc(g,b,c,d)};m.prototype.GetAttributeInt16ForAllPoints=m.prototype.GetAttributeInt16ForAllPoints=function(b,c,d){var g=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!Ac(g,b,c,d)};m.prototype.GetAttributeUInt16ForAllPoints=m.prototype.GetAttributeUInt16ForAllPoints=function(b,c,d){var g=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&
(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!Bc(g,b,c,d)};m.prototype.GetAttributeInt32ForAllPoints=m.prototype.GetAttributeInt32ForAllPoints=function(b,c,d){var g=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!Cc(g,b,c,d)};m.prototype.GetAttributeUInt32ForAllPoints=m.prototype.GetAttributeUInt32ForAllPoints=function(b,c,d){var g=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===
typeof d&&(d=d.ptr);return!!Dc(g,b,c,d)};m.prototype.GetAttributeDataArrayForAllPoints=m.prototype.GetAttributeDataArrayForAllPoints=function(b,c,d,g,t){var aa=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);g&&"object"===typeof g&&(g=g.ptr);t&&"object"===typeof t&&(t=t.ptr);return!!Ec(aa,b,c,d,g,t)};m.prototype.SkipAttributeTransform=m.prototype.SkipAttributeTransform=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);Fc(c,
b)};m.prototype.GetEncodedGeometryType_Deprecated=m.prototype.GetEncodedGeometryType_Deprecated=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return Gc(c,b)};m.prototype.DecodeBufferToPointCloud=m.prototype.DecodeBufferToPointCloud=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);return B(Hc(d,b,c),C)};m.prototype.DecodeBufferToMesh=m.prototype.DecodeBufferToMesh=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===
typeof c&&(c=c.ptr);return B(Ic(d,b,c),C)};m.prototype.__destroy__=m.prototype.__destroy__=function(){Jc(this.ptr)};(function(){function b(){a.ATTRIBUTE_INVALID_TRANSFORM=Kc();a.ATTRIBUTE_NO_TRANSFORM=Lc();a.ATTRIBUTE_QUANTIZATION_TRANSFORM=Mc();a.ATTRIBUTE_OCTAHEDRON_TRANSFORM=Nc();a.INVALID=Oc();a.POSITION=Pc();a.NORMAL=Qc();a.COLOR=Rc();a.TEX_COORD=Sc();a.GENERIC=Tc();a.INVALID_GEOMETRY_TYPE=Uc();a.POINT_CLOUD=Vc();a.TRIANGULAR_MESH=Wc();a.DT_INVALID=Xc();a.DT_INT8=Yc();a.DT_UINT8=Zc();a.DT_INT16=
$c();a.DT_UINT16=ad();a.DT_INT32=bd();a.DT_UINT32=cd();a.DT_INT64=dd();a.DT_UINT64=ed();a.DT_FLOAT32=fd();a.DT_FLOAT64=gd();a.DT_BOOL=hd();a.DT_TYPES_COUNT=id();a.OK=jd();a.DRACO_ERROR=kd();a.IO_ERROR=ld();a.INVALID_PARAMETER=md();a.UNSUPPORTED_VERSION=nd();a.UNKNOWN_VERSION=od()}va?b():oa.unshift(b)})();if("function"===typeof a.onModuleParsed)a.onModuleParsed();a.Decoder.prototype.GetEncodedGeometryType=function(b){if(b.__class__&&b.__class__===a.DecoderBuffer)return a.Decoder.prototype.GetEncodedGeometryType_Deprecated(b);
if(8>b.byteLength)return a.INVALID_GEOMETRY_TYPE;switch(b[7]){case 0:return a.POINT_CLOUD;case 1:return a.TRIANGULAR_MESH;default:return a.INVALID_GEOMETRY_TYPE}};return n.ready}}();"object"===typeof exports&&"object"===typeof module?module.exports=DracoDecoderModule:"function"===typeof define&&define.amd?define([],function(){return DracoDecoderModule}):"object"===typeof exports&&(exports.DracoDecoderModule=DracoDecoderModule);

View File

@@ -43,7 +43,7 @@ namespace gdjs {
// and then must be displayed on a plane in the 3D world:
private _threePlaneTexture: THREE.Texture | null = null;
private _threePlaneGeometry: THREE.PlaneGeometry | null = null;
private _threePlaneMaterial: THREE.MeshBasicMaterial | null = null;
private _threePlaneMaterial: THREE.ShaderMaterial | null = null;
private _threePlaneMesh: THREE.Mesh | null = null;
/**
@@ -195,10 +195,6 @@ namespace gdjs {
// Create the plane that will show this texture.
this._threePlaneGeometry = new THREE.PlaneGeometry(1, 1);
this._threePlaneMaterial = new THREE.MeshBasicMaterial({
side: THREE.FrontSide,
transparent: true,
});
// Create the texture to project on the plane.
// Use a buffer to create a "fake" DataTexture, just so the texture
@@ -221,7 +217,33 @@ namespace gdjs {
this._threePlaneTexture.magFilter = filter;
this._threePlaneTexture.wrapS = THREE.ClampToEdgeWrapping;
this._threePlaneTexture.wrapT = THREE.ClampToEdgeWrapping;
this._threePlaneMaterial.map = this._threePlaneTexture;
// This disable the gamma correction done by THREE as PIXI is already doing it.
const noGammaCorrectionShader: THREE.ShaderMaterialParameters = {
vertexShader: `
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
fragmentShader: `
uniform sampler2D map;
varying vec2 vUv;
void main() {
vec4 texel = texture2D(map, vUv);
gl_FragColor = texel;
}
`,
uniforms: {
map: { value: this._threePlaneTexture },
},
side: THREE.FrontSide,
transparent: true,
};
this._threePlaneMaterial = new THREE.ShaderMaterial(
noGammaCorrectionShader
);
this._threePlaneMaterial;
// Finally, create the mesh shown in the scene.
this._threePlaneMesh = new THREE.Mesh(

View File

@@ -194,6 +194,7 @@ namespace gdjs {
threeTexture.minFilter = THREE.LinearFilter;
threeTexture.wrapS = THREE.RepeatWrapping;
threeTexture.wrapT = THREE.RepeatWrapping;
threeTexture.colorSpace = THREE.SRGBColorSpace;
threeTexture.needsUpdate = true;
const resource = findResourceWithNameAndKind(

View File

@@ -69,6 +69,10 @@ namespace gdjs {
gameCanvas = document.createElement('canvas');
this._threeRenderer = new THREE.WebGLRenderer({
canvas: gameCanvas,
antialias:
this._game.getAntialiasingMode() !== 'none' &&
(this._game.isAntialisingEnabledOnMobile() ||
!gdjs.evtTools.common.isMobile()),
});
this._threeRenderer.autoClear = false;
this._threeRenderer.setSize(

View File

@@ -198,6 +198,10 @@ namespace gdjs {
);
isFirstRender = false;
} else {
// It's important to set the background to null, as maybe the first rendered
// layer has changed and so the Three.js scene background must be reset.
threeScene.background = null;
}
// Clear the depth as each layer is independent and display on top of the previous one,

File diff suppressed because one or more lines are too long

View File

@@ -153,6 +153,8 @@ namespace gdjs {
_adaptGameResolutionAtRuntime: boolean;
_scaleMode: 'linear' | 'nearest';
_pixelsRounding: boolean;
_antialiasingMode: 'none' | 'MSAA';
_isAntialisingEnabledOnMobile: boolean;
/**
* Game loop management (see startGameLoop method)
*/
@@ -245,6 +247,8 @@ namespace gdjs {
this._adaptGameResolutionAtRuntime = this._data.properties.adaptGameResolutionAtRuntime;
this._scaleMode = data.properties.scaleMode || 'linear';
this._pixelsRounding = this._data.properties.pixelsRounding;
this._antialiasingMode = this._data.properties.antialiasingMode;
this._isAntialisingEnabledOnMobile = this._data.properties.antialisingEnabledOnMobile;
this._renderer = new gdjs.RuntimeGameRenderer(
this,
this._options.forceFullscreen || false
@@ -637,6 +641,20 @@ namespace gdjs {
return this._pixelsRounding;
}
/**
* Return the antialiasing mode used by the game ("none" or "MSAA").
*/
getAntialiasingMode(): 'none' | 'MSAA' {
return this._antialiasingMode;
}
/**
* Return true if antialising is enabled on mobiles.
*/
isAntialisingEnabledOnMobile(): boolean {
return this._isAntialisingEnabledOnMobile;
}
/**
* Set or unset the game as paused.
* When paused, the game won't step and will be freezed. Useful for debugging.

View File

@@ -1,7 +1,9 @@
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
import { GLTFLoader, GLTF } from 'three/examples/jsm/loaders/GLTFLoader';
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader';
import * as SkeletonUtils from 'three/examples/jsm/utils/SkeletonUtils';
declare global {
namespace THREE_ADDONS {
export { GLTFLoader };
export { GLTFLoader, GLTF, DRACOLoader, SkeletonUtils };
}
}

View File

@@ -107,16 +107,26 @@ declare interface ExternalLayoutData {
declare interface InstanceData {
persistentUuid: string;
angle: number;
customSize: boolean;
height: number;
layer: string;
locked: boolean;
name: string;
width: number;
x: number;
y: number;
z?: number;
angle: number;
rotationX?: number;
rotationY?: number;
zOrder: number;
customSize: boolean;
width: number;
height: number;
depth?: number;
numberProperties: InstanceNumberProperty[];
stringProperties: InstanceStringProperty[];
initialVariables: RootVariableData[];
@@ -180,6 +190,8 @@ declare interface ProjectPropertiesData {
projectFile: string;
scaleMode: 'linear' | 'nearest';
pixelsRounding: boolean;
antialiasingMode: 'none' | 'MSAA';
antialisingEnabledOnMobile: boolean;
sizeOnStartupMode: string;
useExternalSourceFiles: boolean;
version: string;

41
GDJS/package-lock.json generated
View File

@@ -14,7 +14,7 @@
"@types/mocha": "^5.2.7",
"@types/node": "^14.11.1",
"@types/sinon": "^10.0.13",
"@types/three": "0.150.1",
"@types/three": "0.152.0",
"better-docs": "^2.3.2",
"esbuild": "^0.13.12",
"lebab": "^3.1.0",
@@ -24,7 +24,6 @@
"prettier": "2.1.2",
"recursive-readdir": "^2.2.2",
"shelljs": "^0.8.4",
"three": "0.151.2",
"typedoc": "^0.22.11",
"typedoc-plugin-reference-excluder": "^1.0.0",
"typescript": "4.3.2"
@@ -730,6 +729,12 @@
"@pixi/settings": "6.1.2"
}
},
"node_modules/@tweenjs/tween.js": {
"version": "18.6.4",
"resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-18.6.4.tgz",
"integrity": "sha512-lB9lMjuqjtuJrx7/kOkqQBtllspPIN+96OvTCeJ2j5FEzinoAXTdAMFnDAQT1KVPRlnYfBrqxtqP66vDM40xxQ==",
"dev": true
},
"node_modules/@types/babel-types": {
"version": "7.0.9",
"resolved": "https://registry.npmjs.org/@types/babel-types/-/babel-types-7.0.9.tgz",
@@ -791,11 +796,12 @@
"dev": true
},
"node_modules/@types/three": {
"version": "0.150.1",
"resolved": "https://registry.npmjs.org/@types/three/-/three-0.150.1.tgz",
"integrity": "sha512-ZXS1M3brsfAAbTeeUEt0defPi98yWQuEyPBnvjEYY1dCEYfJnFaww0mYgRpJ9JVfmtwRxqISpVcv/g/0lMl1DQ==",
"version": "0.152.0",
"resolved": "https://registry.npmjs.org/@types/three/-/three-0.152.0.tgz",
"integrity": "sha512-9QdaV5bfZEqeQi0xkXLdnoJt7lgYZbppdBAgJSWRicdtZoCYJ34nS2QkdeuzXt+UXExofk4OWqMzdX71HeDOVg==",
"dev": true,
"dependencies": {
"@tweenjs/tween.js": "~18.6.4",
"@types/stats.js": "*",
"@types/webxr": "*",
"fflate": "~0.6.9",
@@ -2936,12 +2942,6 @@
"node": ">=4"
}
},
"node_modules/three": {
"version": "0.151.2",
"resolved": "https://registry.npmjs.org/three/-/three-0.151.2.tgz",
"integrity": "sha512-tGaLRP2H6++tj2JumHD25slhFAx0C619rl6G6Utjq7JTrDGJwDxpu+J2XpnYIUMfEmhNvIFDQJfp79JtKmNBWw==",
"dev": true
},
"node_modules/tmp": {
"version": "0.0.33",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
@@ -3818,6 +3818,12 @@
"url": "^0.11.0"
}
},
"@tweenjs/tween.js": {
"version": "18.6.4",
"resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-18.6.4.tgz",
"integrity": "sha512-lB9lMjuqjtuJrx7/kOkqQBtllspPIN+96OvTCeJ2j5FEzinoAXTdAMFnDAQT1KVPRlnYfBrqxtqP66vDM40xxQ==",
"dev": true
},
"@types/babel-types": {
"version": "7.0.9",
"resolved": "https://registry.npmjs.org/@types/babel-types/-/babel-types-7.0.9.tgz",
@@ -3879,11 +3885,12 @@
"dev": true
},
"@types/three": {
"version": "0.150.1",
"resolved": "https://registry.npmjs.org/@types/three/-/three-0.150.1.tgz",
"integrity": "sha512-ZXS1M3brsfAAbTeeUEt0defPi98yWQuEyPBnvjEYY1dCEYfJnFaww0mYgRpJ9JVfmtwRxqISpVcv/g/0lMl1DQ==",
"version": "0.152.0",
"resolved": "https://registry.npmjs.org/@types/three/-/three-0.152.0.tgz",
"integrity": "sha512-9QdaV5bfZEqeQi0xkXLdnoJt7lgYZbppdBAgJSWRicdtZoCYJ34nS2QkdeuzXt+UXExofk4OWqMzdX71HeDOVg==",
"dev": true,
"requires": {
"@tweenjs/tween.js": "~18.6.4",
"@types/stats.js": "*",
"@types/webxr": "*",
"fflate": "~0.6.9",
@@ -5592,12 +5599,6 @@
"has-flag": "^3.0.0"
}
},
"three": {
"version": "0.151.2",
"resolved": "https://registry.npmjs.org/three/-/three-0.151.2.tgz",
"integrity": "sha512-tGaLRP2H6++tj2JumHD25slhFAx0C619rl6G6Utjq7JTrDGJwDxpu+J2XpnYIUMfEmhNvIFDQJfp79JtKmNBWw==",
"dev": true
},
"tmp": {
"version": "0.0.33",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",

View File

@@ -10,7 +10,7 @@
"@types/mocha": "^5.2.7",
"@types/node": "^14.11.1",
"@types/sinon": "^10.0.13",
"@types/three": "0.150.1",
"@types/three": "0.152.0",
"better-docs": "^2.3.2",
"esbuild": "^0.13.12",
"lebab": "^3.1.0",
@@ -20,7 +20,6 @@
"prettier": "2.1.2",
"recursive-readdir": "^2.2.2",
"shelljs": "^0.8.4",
"three": "0.151.2",
"typedoc": "^0.22.11",
"typedoc-plugin-reference-excluder": "^1.0.0",
"typescript": "4.3.2"

View File

@@ -24,13 +24,14 @@ const untransformedPaths = [
// GDJS prebuilt files:
'GDJS/Runtime/pixi-renderers/pixi.js',
'GDJS/Runtime/pixi-renderers/three.js',
'GDJS/Runtime/pixi-renderers/ThreeAddons.js',
'GDJS/Runtime/pixi-renderers/draco/gltf/draco_wasm_wrapper.js',
'GDJS/Runtime/fontfaceobserver-font-manager/fontfaceobserver.js',
'GDJS/Runtime/Cordova',
'GDJS/Runtime/Electron',
'GDJS/Runtime/FacebookInstantGames',
'GDJS/Runtime/libs/CocoonJS',
'GDJS/Runtime/libs/rbush.js',
'GDJS/Runtime/libs/ThreeAddons.js',
// Extensions pre-built files:
'Extensions/Leaderboards/sha256.js',

View File

@@ -18,6 +18,8 @@ gdjs.getPixiRuntimeGame = (settings) => {
scaleMode: 'linear',
pixelsRounding: false,
sizeOnStartupMode: '',
antialiasingMode: 'MSAA',
antialisingEnabledOnMobile: false,
useExternalSourceFiles: true,
version: '1.0.0',
name: 'Test game',

View File

@@ -22,6 +22,8 @@ gdjs.getPixiRuntimeGameWithAssets = () => {
scaleMode: 'linear',
pixelsRounding: false,
sizeOnStartupMode: 'adaptWidth',
antialiasingMode: 'MSAA',
antialisingEnabledOnMobile: false,
useExternalSourceFiles: true,
version: '1.0.0',
name: 'Test game with real assets',

View File

@@ -445,6 +445,10 @@ interface Project {
boolean GetPixelsRounding();
void SetSizeOnStartupMode([Const] DOMString orientation);
[Const, Ref] DOMString GetSizeOnStartupMode();
void SetAntialiasingMode([Const] DOMString antialiasingMode);
[Const, Ref] DOMString GetAntialiasingMode();
void SetAntialisingEnabledOnMobile(boolean pixelsRounding);
boolean IsAntialisingEnabledOnMobile();
long GetMaximumFPS();
void SetMaximumFPS(long fps);
long GetMinimumFPS();
@@ -630,6 +634,7 @@ interface gdObject {
[Const, Ref] DOMString GetType();
void SetTags([Const] DOMString tags);
[Const, Ref] DOMString GetTags();
boolean Is3DObject();
[Ref] ObjectConfiguration GetConfiguration();
@@ -1055,8 +1060,14 @@ interface InitialInstance {
void SetX(double x);
double GetY();
void SetY(double y);
double GetZ();
void SetZ(double z);
double GetAngle();
void SetAngle(double angle);
double GetRotationX();
void SetRotationX(double rotationX);
double GetRotationY();
void SetRotationY(double rotationY);
boolean IsLocked();
void SetLocked(boolean lock);
boolean IsSealed();
@@ -1068,11 +1079,15 @@ interface InitialInstance {
void SetHasCustomSize(boolean enable);
boolean HasCustomSize();
void SetHasCustomDepth(boolean enable);
boolean HasCustomDepth();
void SetCustomWidth(double width);
double GetCustomWidth();
void SetCustomHeight(double height);
double GetCustomHeight();
void SetCustomDepth(double depth);
double GetCustomDepth();
[Ref] InitialInstance ResetPersistentUuid();
@@ -3004,6 +3019,34 @@ interface SpriteObject {
};
SpriteObject implements ObjectConfiguration;
interface Model3DAnimation {
void Model3DAnimation();
void SetName([Const] DOMString name);
[Const, Ref] DOMString GetName();
void SetSource([Const] DOMString name);
[Const, Ref] DOMString GetSource();
void SetShouldLoop(boolean shouldLoop);
boolean ShouldLoop();
};
interface Model3DObjectConfiguration {
void Model3DObjectConfiguration();
void AddAnimation([Const, Ref] Model3DAnimation animation);
[Ref] Model3DAnimation GetAnimation(unsigned long index);
boolean HasAnimationNamed([Const] DOMString name);
unsigned long GetAnimationsCount();
void RemoveAnimation(unsigned long index);
void RemoveAllAnimations();
boolean HasNoAnimations();
void SwapAnimations(unsigned long first, unsigned long second);
void MoveAnimation(unsigned long oldIndex, unsigned long newIndex);
};
Model3DObjectConfiguration implements ObjectConfiguration;
interface Vector2f {
void Vector2f();

View File

@@ -100,6 +100,7 @@
#include "../../Extensions/TextEntryObject/TextEntryObject.h"
#include "../../Extensions/TextObject/TextObject.h"
#include "../../Extensions/TiledSpriteObject/TiledSpriteObject.h"
#include "../../Extensions/3D/Model3DObjectConfiguration.h"
#include "BehaviorJsImplementation.h"
#include "BehaviorSharedDataJsImplementation.h"
#include "ObjectJsImplementation.h"

View File

@@ -147,6 +147,9 @@ var adaptNamingConventions = function (gd) {
gd.asCustomObjectConfiguration = function (evt) {
return gd.castObject(evt, gd.CustomObjectConfiguration);
};
gd.asModel3DConfiguration = function (evt) {
return gd.castObject(evt, gd.Model3DObjectConfiguration);
};
gd.asImageResource = function (evt) {
return gd.castObject(evt, gd.ImageResource);

View File

@@ -102,3 +102,4 @@ target_link_libraries(GD Shopify)
target_link_libraries(GD PathfindingBehavior)
target_link_libraries(GD PhysicsBehavior)
target_link_libraries(GD ParticleSystem)
target_link_libraries(GD Scene3D)

View File

@@ -597,6 +597,8 @@ describe('libGD.js', function () {
expect(initialInstance.getCustomWidth()).toBe(34);
initialInstance.setCustomHeight(30);
expect(initialInstance.getCustomHeight()).toBe(30);
expect(initialInstance.hasCustomDepth()).toBe(false);
});
it('Sprite object custom properties', function () {
initialInstance.updateCustomProperty('animation', '2', project, layout);
@@ -622,13 +624,154 @@ describe('libGD.js', function () {
expect(initialInstance2.getObjectName()).toBe('MySpriteObject');
expect(initialInstance2.getX()).toBe(150);
expect(initialInstance2.getY()).toBe(140);
expect(initialInstance2.getZ()).toBe(0);
expect(initialInstance2.getAngle()).toBe(45);
expect(initialInstance2.getRotationX()).toBe(0);
expect(initialInstance2.getRotationY()).toBe(0);
expect(initialInstance2.getZOrder()).toBe(12);
expect(initialInstance2.getLayer()).toBe('MyLayer');
expect(initialInstance2.isLocked()).toBe(true);
expect(initialInstance2.hasCustomSize()).toBe(true);
expect(initialInstance2.hasCustomDepth()).toBe(false);
expect(initialInstance2.getCustomWidth()).toBe(34);
expect(initialInstance2.getCustomHeight()).toBe(30);
expect(initialInstance2.getCustomDepth()).toBe(0);
});
it('can have 3D properties migrated from number properties', function () {
const element = gd.Serializer.fromJSObject({
"angle": 2,
"customSize": true,
"height": 100,
"layer": "",
"name": "Walls",
"persistentUuid": "1075df65-af84-472d-a431-47d6f7a5cb63",
"width": 950,
"x": -100,
"y": -75,
"zOrder": 1,
"numberProperties": [
{
"name": "depth", // This indicates there is a custom depth.
"value": 300
},
{
"name": "z",
"value": 12
},
{
"name": "rotationX",
"value": 1
},
{
"name": "rotationY",
"value": 3
}
],
"stringProperties": [],
"initialVariables": []
});
const migratedInstance = layout
.getInitialInstances()
.insertNewInitialInstance();
migratedInstance.unserializeFrom(element);
element.delete();
expect(migratedInstance.getX()).toBe(-100);
expect(migratedInstance.getY()).toBe(-75);
expect(migratedInstance.getZ()).toBe(12);
expect(migratedInstance.getAngle()).toBe(2);
expect(migratedInstance.getRotationX()).toBe(1);
expect(migratedInstance.getRotationY()).toBe(3);
expect(migratedInstance.hasCustomSize()).toBe(true);
expect(migratedInstance.hasCustomDepth()).toBe(true);
expect(migratedInstance.getCustomWidth()).toBe(950);
expect(migratedInstance.getCustomHeight()).toBe(100);
expect(migratedInstance.getCustomDepth()).toBe(300);
});
it('can have depth without a custom size', function () {
const element = gd.Serializer.fromJSObject({
"angle": 2,
"customSize": false,
"height": 100,
"layer": "",
"name": "Walls",
"persistentUuid": "1075df65-af84-472d-a431-47d6f7a5cb63",
"width": 950,
"x": -100,
"y": -75,
"zOrder": 1,
"z": 12,
"depth": 300, // This indicates there is a custom depth.
"rotationX": 1,
"rotationY": 3,
"numberProperties": [
],
"stringProperties": [],
"initialVariables": []
});
const instanceWithJustDepth = layout
.getInitialInstances()
.insertNewInitialInstance();
instanceWithJustDepth.unserializeFrom(element);
element.delete();
expect(instanceWithJustDepth.getX()).toBe(-100);
expect(instanceWithJustDepth.getY()).toBe(-75);
expect(instanceWithJustDepth.getZ()).toBe(12);
expect(instanceWithJustDepth.getAngle()).toBe(2);
expect(instanceWithJustDepth.getRotationX()).toBe(1);
expect(instanceWithJustDepth.getRotationY()).toBe(3);
expect(instanceWithJustDepth.hasCustomSize()).toBe(false);
expect(instanceWithJustDepth.hasCustomDepth()).toBe(true);
expect(instanceWithJustDepth.getCustomDepth()).toBe(300);
expect(instanceWithJustDepth.getCustomWidth()).toBe(950);
expect(instanceWithJustDepth.getCustomHeight()).toBe(100);
});
it('can have 3D properties', function () {
const initialInstanceIn3D = layout.getInitialInstances().insertNewInitialInstance();
initialInstanceIn3D.setX(40);
initialInstanceIn3D.setY(41);
initialInstanceIn3D.setZ(42);
initialInstanceIn3D.setAngle(43);
initialInstanceIn3D.setRotationX(44);
initialInstanceIn3D.setRotationY(45);
initialInstanceIn3D.setHasCustomSize(true);
initialInstanceIn3D.setHasCustomDepth(true);
initialInstanceIn3D.setCustomWidth(46);
initialInstanceIn3D.setCustomHeight(47);
initialInstanceIn3D.setCustomDepth(48);
expect(initialInstanceIn3D.getX()).toBe(40);
expect(initialInstanceIn3D.getY()).toBe(41);
expect(initialInstanceIn3D.getZ()).toBe(42);
expect(initialInstanceIn3D.getAngle()).toBe(43);
expect(initialInstanceIn3D.getRotationX()).toBe(44);
expect(initialInstanceIn3D.getRotationY()).toBe(45);
expect(initialInstanceIn3D.hasCustomSize()).toBe(true);
expect(initialInstanceIn3D.hasCustomDepth()).toBe(true);
expect(initialInstanceIn3D.getCustomWidth()).toBe(46);
expect(initialInstanceIn3D.getCustomHeight()).toBe(47);
expect(initialInstanceIn3D.getCustomDepth()).toBe(48);
const element = new gd.SerializerElement();
initialInstanceIn3D.serializeTo(element);
const initialInstance2 = layout
.getInitialInstances()
.insertNewInitialInstance();
initialInstance2.unserializeFrom(element);
expect(initialInstance2.getX()).toBe(40);
expect(initialInstance2.getY()).toBe(41);
expect(initialInstance2.getZ()).toBe(42);
expect(initialInstance2.getAngle()).toBe(43);
expect(initialInstance2.getRotationX()).toBe(44);
expect(initialInstance2.getRotationY()).toBe(45);
expect(initialInstance2.hasCustomSize()).toBe(true);
expect(initialInstance2.hasCustomDepth()).toBe(true);
expect(initialInstance2.getCustomWidth()).toBe(46);
expect(initialInstance2.getCustomHeight()).toBe(47);
expect(initialInstance2.getCustomDepth()).toBe(48);
});
it('can be serialized with a persistent UUID called persistentUuid', function () {
const initialInstance = new gd.InitialInstance();

View File

@@ -188,6 +188,7 @@ type ParticleEmitterObject_RendererType = 0 | 1 | 2`
` asParticleEmitterConfiguration(gdObjectConfiguration): gdParticleEmitterObject;`,
` asObjectJsImplementation(gdObjectConfiguration): gdObjectJsImplementation;`,
` asCustomObjectConfiguration(gdObjectConfiguration): gdCustomObjectConfiguration;`,
` asModel3DConfiguration(gdObjectConfiguration): gdModel3DObjectConfiguration;`,
'',
` asImageResource(gdResource): gdImageResource;`,
'',

View File

@@ -7,8 +7,14 @@ declare class gdInitialInstance {
setX(x: number): void;
getY(): number;
setY(y: number): void;
getZ(): number;
setZ(z: number): void;
getAngle(): number;
setAngle(angle: number): void;
getRotationX(): number;
setRotationX(rotationX: number): void;
getRotationY(): number;
setRotationY(rotationY: number): void;
isLocked(): boolean;
setLocked(lock: boolean): void;
isSealed(): boolean;
@@ -19,10 +25,14 @@ declare class gdInitialInstance {
setLayer(layer: string): void;
setHasCustomSize(enable: boolean): void;
hasCustomSize(): boolean;
setHasCustomDepth(enable: boolean): void;
hasCustomDepth(): boolean;
setCustomWidth(width: number): void;
getCustomWidth(): number;
setCustomHeight(height: number): void;
getCustomHeight(): number;
setCustomDepth(depth: number): void;
getCustomDepth(): number;
resetPersistentUuid(): gdInitialInstance;
updateCustomProperty(name: string, value: string, project: gdProject, layout: gdLayout): void;
getCustomProperties(project: gdProject, layout: gdLayout): gdMapStringPropertyDescriptor;

View File

@@ -0,0 +1,12 @@
// Automatically generated by GDevelop.js/scripts/generate-types.js
declare class gdModel3DAnimation {
constructor(): void;
setName(name: string): void;
getName(): string;
setSource(name: string): void;
getSource(): string;
setShouldLoop(shouldLoop: boolean): void;
shouldLoop(): boolean;
delete(): void;
ptr: number;
};

View File

@@ -0,0 +1,15 @@
// Automatically generated by GDevelop.js/scripts/generate-types.js
declare class gdModel3DObjectConfiguration extends gdObjectConfiguration {
constructor(): void;
addAnimation(animation: gdModel3DAnimation): void;
getAnimation(index: number): gdModel3DAnimation;
hasAnimationNamed(name: string): boolean;
getAnimationsCount(): number;
removeAnimation(index: number): void;
removeAllAnimations(): void;
hasNoAnimations(): boolean;
swapAnimations(first: number, second: number): void;
moveAnimation(oldIndex: number, newIndex: number): void;
delete(): void;
ptr: number;
};

View File

@@ -10,6 +10,7 @@ declare class gdObject {
getType(): string;
setTags(tags: string): void;
getTags(): string;
is3DObject(): boolean;
getConfiguration(): gdObjectConfiguration;
getVariables(): gdVariablesContainer;
getEffects(): gdEffectsContainer;

View File

@@ -40,6 +40,10 @@ declare class gdProject extends gdObjectsContainer {
getPixelsRounding(): boolean;
setSizeOnStartupMode(orientation: string): void;
getSizeOnStartupMode(): string;
setAntialiasingMode(antialiasingMode: string): void;
getAntialiasingMode(): string;
setAntialisingEnabledOnMobile(pixelsRounding: boolean): void;
isAntialisingEnabledOnMobile(): boolean;
getMaximumFPS(): number;
setMaximumFPS(fps: number): void;
getMinimumFPS(): number;

View File

@@ -32,6 +32,7 @@ declare class libGDevelop {
asParticleEmitterConfiguration(gdObjectConfiguration): gdParticleEmitterObject;
asObjectJsImplementation(gdObjectConfiguration): gdObjectJsImplementation;
asCustomObjectConfiguration(gdObjectConfiguration): gdCustomObjectConfiguration;
asModel3DConfiguration(gdObjectConfiguration): gdModel3DObjectConfiguration;
asImageResource(gdResource): gdImageResource;
@@ -215,6 +216,8 @@ declare class libGDevelop {
Direction: Class<gdDirection>;
Animation: Class<gdAnimation>;
SpriteObject: Class<gdSpriteObject>;
Model3DAnimation: Class<gdModel3DAnimation>;
Model3DObjectConfiguration: Class<gdModel3DObjectConfiguration>;
Vector2f: Class<gdVector2f>;
VectorVector2f: Class<gdVectorVector2f>;
TextObject: Class<gdTextObject>;

View File

@@ -0,0 +1,613 @@
// MIT License
// Copyright © 2010-2023 three.js authors
import {
BufferAttribute,
BufferGeometry,
Color,
FileLoader,
Loader,
LinearSRGBColorSpace,
SRGBColorSpace
} from 'three';
const _taskCache = new WeakMap();
class DRACOLoader extends Loader {
constructor( manager ) {
super( manager );
this.decoderPath = '';
this.decoderConfig = {};
this.decoderBinary = null;
this.decoderPending = null;
this.workerLimit = 4;
this.workerPool = [];
this.workerNextTaskID = 1;
this.workerSourceURL = '';
this.defaultAttributeIDs = {
position: 'POSITION',
normal: 'NORMAL',
color: 'COLOR',
uv: 'TEX_COORD'
};
this.defaultAttributeTypes = {
position: 'Float32Array',
normal: 'Float32Array',
color: 'Float32Array',
uv: 'Float32Array'
};
}
setDecoderPath( path ) {
this.decoderPath = path;
return this;
}
setDecoderConfig( config ) {
this.decoderConfig = config;
return this;
}
setWorkerLimit( workerLimit ) {
this.workerLimit = workerLimit;
return this;
}
load( url, onLoad, onProgress, onError ) {
const loader = new FileLoader( this.manager );
loader.setPath( this.path );
loader.setResponseType( 'arraybuffer' );
loader.setRequestHeader( this.requestHeader );
loader.setWithCredentials( this.withCredentials );
loader.load( url, ( buffer ) => {
this.parse( buffer, onLoad, onError );
}, onProgress, onError );
}
parse( buffer, onLoad, onError ) {
this.decodeDracoFile( buffer, onLoad, null, null, SRGBColorSpace ).catch( onError );
}
decodeDracoFile( buffer, callback, attributeIDs, attributeTypes, vertexColorSpace = LinearSRGBColorSpace ) {
const taskConfig = {
attributeIDs: attributeIDs || this.defaultAttributeIDs,
attributeTypes: attributeTypes || this.defaultAttributeTypes,
useUniqueIDs: !! attributeIDs,
vertexColorSpace: vertexColorSpace,
};
return this.decodeGeometry( buffer, taskConfig ).then( callback );
}
decodeGeometry( buffer, taskConfig ) {
const taskKey = JSON.stringify( taskConfig );
// Check for an existing task using this buffer. A transferred buffer cannot be transferred
// again from this thread.
if ( _taskCache.has( buffer ) ) {
const cachedTask = _taskCache.get( buffer );
if ( cachedTask.key === taskKey ) {
return cachedTask.promise;
} else if ( buffer.byteLength === 0 ) {
// Technically, it would be possible to wait for the previous task to complete,
// transfer the buffer back, and decode again with the second configuration. That
// is complex, and I don't know of any reason to decode a Draco buffer twice in
// different ways, so this is left unimplemented.
throw new Error(
'THREE.DRACOLoader: Unable to re-decode a buffer with different ' +
'settings. Buffer has already been transferred.'
);
}
}
//
let worker;
const taskID = this.workerNextTaskID ++;
const taskCost = buffer.byteLength;
// Obtain a worker and assign a task, and construct a geometry instance
// when the task completes.
const geometryPending = this._getWorker( taskID, taskCost )
.then( ( _worker ) => {
worker = _worker;
return new Promise( ( resolve, reject ) => {
worker._callbacks[ taskID ] = { resolve, reject };
worker.postMessage( { type: 'decode', id: taskID, taskConfig, buffer }, [ buffer ] );
// this.debug();
} );
} )
.then( ( message ) => this._createGeometry( message.geometry ) );
// Remove task from the task list.
// Note: replaced '.finally()' with '.catch().then()' block - iOS 11 support (#19416)
geometryPending
.catch( () => true )
.then( () => {
if ( worker && taskID ) {
this._releaseTask( worker, taskID );
// this.debug();
}
} );
// Cache the task result.
_taskCache.set( buffer, {
key: taskKey,
promise: geometryPending
} );
return geometryPending;
}
_createGeometry( geometryData ) {
const geometry = new BufferGeometry();
if ( geometryData.index ) {
geometry.setIndex( new BufferAttribute( geometryData.index.array, 1 ) );
}
for ( let i = 0; i < geometryData.attributes.length; i ++ ) {
const result = geometryData.attributes[ i ];
const name = result.name;
const array = result.array;
const itemSize = result.itemSize;
const attribute = new BufferAttribute( array, itemSize );
if ( name === 'color' ) {
this._assignVertexColorSpace( attribute, result.vertexColorSpace );
}
geometry.setAttribute( name, attribute );
}
return geometry;
}
_assignVertexColorSpace( attribute, inputColorSpace ) {
// While .drc files do not specify colorspace, the only 'official' tooling
// is PLY and OBJ converters, which use sRGB. We'll assume sRGB when a .drc
// file is passed into .load() or .parse(). GLTFLoader uses internal APIs
// to decode geometry, and vertex colors are already Linear-sRGB in there.
if ( inputColorSpace !== SRGBColorSpace ) return;
const _color = new Color();
for ( let i = 0, il = attribute.count; i < il; i ++ ) {
_color.fromBufferAttribute( attribute, i ).convertSRGBToLinear();
attribute.setXYZ( i, _color.r, _color.g, _color.b );
}
}
_loadLibrary( url, responseType ) {
const loader = new FileLoader( this.manager );
loader.setPath( this.decoderPath );
loader.setResponseType( responseType );
loader.setWithCredentials( this.withCredentials );
return new Promise( ( resolve, reject ) => {
loader.load( url, resolve, undefined, reject );
} );
}
preload() {
this._initDecoder();
return this;
}
_initDecoder() {
if ( this.decoderPending ) return this.decoderPending;
const useJS = typeof WebAssembly !== 'object' || this.decoderConfig.type === 'js';
const librariesPending = [];
if ( useJS ) {
librariesPending.push( this._loadLibrary( 'draco_decoder.js', 'text' ) );
} else {
librariesPending.push( this._loadLibrary( 'draco_wasm_wrapper.js', 'text' ) );
librariesPending.push( this._loadLibrary( 'draco_decoder.wasm', 'arraybuffer' ) );
}
this.decoderPending = Promise.all( librariesPending )
.then( ( libraries ) => {
const jsContent = libraries[ 0 ];
if ( ! useJS ) {
this.decoderConfig.wasmBinary = libraries[ 1 ];
}
const fn = DRACOWorker.toString();
const body = [
'/* draco decoder */',
jsContent,
'',
'/* worker */',
fn.substring( fn.indexOf( '{' ) + 1, fn.lastIndexOf( '}' ) )
].join( '\n' );
this.workerSourceURL = URL.createObjectURL( new Blob( [ body ] ) );
} );
return this.decoderPending;
}
_getWorker( taskID, taskCost ) {
return this._initDecoder().then( () => {
if ( this.workerPool.length < this.workerLimit ) {
const worker = new Worker( this.workerSourceURL );
worker._callbacks = {};
worker._taskCosts = {};
worker._taskLoad = 0;
worker.postMessage( { type: 'init', decoderConfig: this.decoderConfig } );
worker.onmessage = function ( e ) {
const message = e.data;
switch ( message.type ) {
case 'decode':
worker._callbacks[ message.id ].resolve( message );
break;
case 'error':
worker._callbacks[ message.id ].reject( message );
break;
default:
console.error( 'THREE.DRACOLoader: Unexpected message, "' + message.type + '"' );
}
};
this.workerPool.push( worker );
} else {
this.workerPool.sort( function ( a, b ) {
return a._taskLoad > b._taskLoad ? - 1 : 1;
} );
}
const worker = this.workerPool[ this.workerPool.length - 1 ];
worker._taskCosts[ taskID ] = taskCost;
worker._taskLoad += taskCost;
return worker;
} );
}
_releaseTask( worker, taskID ) {
worker._taskLoad -= worker._taskCosts[ taskID ];
delete worker._callbacks[ taskID ];
delete worker._taskCosts[ taskID ];
}
debug() {
console.log( 'Task load: ', this.workerPool.map( ( worker ) => worker._taskLoad ) );
}
dispose() {
for ( let i = 0; i < this.workerPool.length; ++ i ) {
this.workerPool[ i ].terminate();
}
this.workerPool.length = 0;
if ( this.workerSourceURL !== '' ) {
URL.revokeObjectURL( this.workerSourceURL );
}
return this;
}
}
/* WEB WORKER */
function DRACOWorker() {
let decoderConfig;
let decoderPending;
onmessage = function ( e ) {
const message = e.data;
switch ( message.type ) {
case 'init':
decoderConfig = message.decoderConfig;
decoderPending = new Promise( function ( resolve/*, reject*/ ) {
decoderConfig.onModuleLoaded = function ( draco ) {
// Module is Promise-like. Wrap before resolving to avoid loop.
resolve( { draco: draco } );
};
DracoDecoderModule( decoderConfig ); // eslint-disable-line no-undef
} );
break;
case 'decode':
const buffer = message.buffer;
const taskConfig = message.taskConfig;
decoderPending.then( ( module ) => {
const draco = module.draco;
const decoder = new draco.Decoder();
try {
const geometry = decodeGeometry( draco, decoder, new Int8Array( buffer ), taskConfig );
const buffers = geometry.attributes.map( ( attr ) => attr.array.buffer );
if ( geometry.index ) buffers.push( geometry.index.array.buffer );
self.postMessage( { type: 'decode', id: message.id, geometry }, buffers );
} catch ( error ) {
console.error( error );
self.postMessage( { type: 'error', id: message.id, error: error.message } );
} finally {
draco.destroy( decoder );
}
} );
break;
}
};
function decodeGeometry( draco, decoder, array, taskConfig ) {
const attributeIDs = taskConfig.attributeIDs;
const attributeTypes = taskConfig.attributeTypes;
let dracoGeometry;
let decodingStatus;
const geometryType = decoder.GetEncodedGeometryType( array );
if ( geometryType === draco.TRIANGULAR_MESH ) {
dracoGeometry = new draco.Mesh();
decodingStatus = decoder.DecodeArrayToMesh( array, array.byteLength, dracoGeometry );
} else if ( geometryType === draco.POINT_CLOUD ) {
dracoGeometry = new draco.PointCloud();
decodingStatus = decoder.DecodeArrayToPointCloud( array, array.byteLength, dracoGeometry );
} else {
throw new Error( 'THREE.DRACOLoader: Unexpected geometry type.' );
}
if ( ! decodingStatus.ok() || dracoGeometry.ptr === 0 ) {
throw new Error( 'THREE.DRACOLoader: Decoding failed: ' + decodingStatus.error_msg() );
}
const geometry = { index: null, attributes: [] };
// Gather all vertex attributes.
for ( const attributeName in attributeIDs ) {
const attributeType = self[ attributeTypes[ attributeName ] ];
let attribute;
let attributeID;
// A Draco file may be created with default vertex attributes, whose attribute IDs
// are mapped 1:1 from their semantic name (POSITION, NORMAL, ...). Alternatively,
// a Draco file may contain a custom set of attributes, identified by known unique
// IDs. glTF files always do the latter, and `.drc` files typically do the former.
if ( taskConfig.useUniqueIDs ) {
attributeID = attributeIDs[ attributeName ];
attribute = decoder.GetAttributeByUniqueId( dracoGeometry, attributeID );
} else {
attributeID = decoder.GetAttributeId( dracoGeometry, draco[ attributeIDs[ attributeName ] ] );
if ( attributeID === - 1 ) continue;
attribute = decoder.GetAttribute( dracoGeometry, attributeID );
}
const attributeResult = decodeAttribute( draco, decoder, dracoGeometry, attributeName, attributeType, attribute );
if ( attributeName === 'color' ) {
attributeResult.vertexColorSpace = taskConfig.vertexColorSpace;
}
geometry.attributes.push( attributeResult );
}
// Add index.
if ( geometryType === draco.TRIANGULAR_MESH ) {
geometry.index = decodeIndex( draco, decoder, dracoGeometry );
}
draco.destroy( dracoGeometry );
return geometry;
}
function decodeIndex( draco, decoder, dracoGeometry ) {
const numFaces = dracoGeometry.num_faces();
const numIndices = numFaces * 3;
const byteLength = numIndices * 4;
const ptr = draco._malloc( byteLength );
decoder.GetTrianglesUInt32Array( dracoGeometry, byteLength, ptr );
const index = new Uint32Array( draco.HEAPF32.buffer, ptr, numIndices ).slice();
draco._free( ptr );
return { array: index, itemSize: 1 };
}
function decodeAttribute( draco, decoder, dracoGeometry, attributeName, attributeType, attribute ) {
const numComponents = attribute.num_components();
const numPoints = dracoGeometry.num_points();
const numValues = numPoints * numComponents;
const byteLength = numValues * attributeType.BYTES_PER_ELEMENT;
const dataType = getDracoDataType( draco, attributeType );
const ptr = draco._malloc( byteLength );
decoder.GetAttributeDataArrayForAllPoints( dracoGeometry, attribute, dataType, byteLength, ptr );
const array = new attributeType( draco.HEAPF32.buffer, ptr, numValues ).slice();
draco._free( ptr );
return {
name: attributeName,
array: array,
itemSize: numComponents
};
}
function getDracoDataType( draco, attributeType ) {
switch ( attributeType ) {
case Float32Array: return draco.DT_FLOAT32;
case Int8Array: return draco.DT_INT8;
case Int16Array: return draco.DT_INT16;
case Int32Array: return draco.DT_INT32;
case Uint8Array: return draco.DT_UINT8;
case Uint16Array: return draco.DT_UINT16;
case Uint32Array: return draco.DT_UINT32;
}
}
}
export { DRACOLoader };

View File

@@ -63,7 +63,7 @@ import {
Vector2,
Vector3,
VectorKeyframeTrack,
sRGBEncoding
SRGBColorSpace
} from 'three';
import { toTrianglesDrawMode } from '../utils/BufferGeometryUtils.js';
@@ -666,7 +666,7 @@ class GLTFMaterialsUnlitExtension {
if ( metallicRoughness.baseColorTexture !== undefined ) {
pending.push( parser.assignTexture( materialParams, 'map', metallicRoughness.baseColorTexture, sRGBEncoding ) );
pending.push( parser.assignTexture( materialParams, 'map', metallicRoughness.baseColorTexture, SRGBColorSpace ) );
}
@@ -947,7 +947,7 @@ class GLTFMaterialsSheenExtension {
if ( extension.sheenColorTexture !== undefined ) {
pending.push( parser.assignTexture( materialParams, 'sheenColorMap', extension.sheenColorTexture, sRGBEncoding ) );
pending.push( parser.assignTexture( materialParams, 'sheenColorMap', extension.sheenColorTexture, SRGBColorSpace ) );
}
@@ -1180,7 +1180,7 @@ class GLTFMaterialsSpecularExtension {
if ( extension.specularColorTexture !== undefined ) {
pending.push( parser.assignTexture( materialParams, 'specularColorMap', extension.specularColorTexture, sRGBEncoding ) );
pending.push( parser.assignTexture( materialParams, 'specularColorMap', extension.specularColorTexture, SRGBColorSpace ) );
}
@@ -1621,8 +1621,6 @@ class GLTFMeshGpuInstancing {
// Just in case
Object3D.prototype.copy.call( instancedMesh, mesh );
// https://github.com/mrdoob/three.js/issues/18334
instancedMesh.frustumCulled = false;
this.parser.assignFinalMaterial( instancedMesh );
instancedMeshes.push( instancedMesh );
@@ -1817,13 +1815,10 @@ class GLTFTextureTransformExtension {
extendTexture( texture, transform ) {
if ( transform.texCoord !== undefined ) {
console.warn( 'THREE.GLTFLoader: Custom UV sets in "' + this.name + '" extension not yet supported.' );
}
if ( transform.offset === undefined && transform.rotation === undefined && transform.scale === undefined ) {
if ( ( transform.texCoord === undefined || transform.texCoord === texture.channel )
&& transform.offset === undefined
&& transform.rotation === undefined
&& transform.scale === undefined ) {
// See https://github.com/mrdoob/three.js/issues/21819.
return texture;
@@ -1832,6 +1827,12 @@ class GLTFTextureTransformExtension {
texture = texture.clone();
if ( transform.texCoord !== undefined ) {
texture.channel = transform.texCoord;
}
if ( transform.offset !== undefined ) {
texture.offset.fromArray( transform.offset );
@@ -2033,7 +2034,9 @@ const ATTRIBUTES = {
NORMAL: 'normal',
TANGENT: 'tangent',
TEXCOORD_0: 'uv',
TEXCOORD_1: 'uv2',
TEXCOORD_1: 'uv1',
TEXCOORD_2: 'uv2',
TEXCOORD_3: 'uv3',
COLOR_0: 'color',
WEIGHTS_0: 'skinWeight',
JOINTS_0: 'skinIndex',
@@ -2995,6 +2998,12 @@ class GLTFParser {
texture.name = textureDef.name || sourceDef.name || '';
if ( texture.name === '' && typeof sourceDef.uri === 'string' && sourceDef.uri.startsWith( 'data:image/' ) === false ) {
texture.name = sourceDef.uri;
}
const samplers = json.samplers || {};
const sampler = samplers[ textureDef.sampler ] || {};
@@ -3113,7 +3122,7 @@ class GLTFParser {
* @param {Object} mapDef
* @return {Promise<Texture>}
*/
assignTexture( materialParams, mapName, mapDef, encoding ) {
assignTexture( materialParams, mapName, mapDef, colorSpace ) {
const parser = this;
@@ -3121,11 +3130,10 @@ class GLTFParser {
if ( ! texture ) return null;
// Materials sample aoMap from UV set 1 and other maps from UV set 0 - this can't be configured
// However, we will copy UV set 0 to UV set 1 on demand for aoMap
if ( mapDef.texCoord !== undefined && mapDef.texCoord != 0 && ! ( mapName === 'aoMap' && mapDef.texCoord == 1 ) ) {
if ( mapDef.texCoord !== undefined && mapDef.texCoord > 0 ) {
console.warn( 'THREE.GLTFLoader: Custom UV set ' + mapDef.texCoord + ' for texture ' + mapName + ' not yet supported.' );
texture = texture.clone();
texture.channel = mapDef.texCoord;
}
@@ -3143,9 +3151,9 @@ class GLTFParser {
}
if ( encoding !== undefined ) {
if ( colorSpace !== undefined ) {
texture.encoding = encoding;
texture.colorSpace = colorSpace;
}
@@ -3205,6 +3213,7 @@ class GLTFParser {
lineMaterial = new LineBasicMaterial();
Material.prototype.copy.call( lineMaterial, material );
lineMaterial.color.copy( material.color );
lineMaterial.map = material.map;
this.cache.add( cacheKey, lineMaterial );
@@ -3250,14 +3259,6 @@ class GLTFParser {
}
// workarounds for mesh and geometry
if ( material.aoMap && geometry.attributes.uv2 === undefined && geometry.attributes.uv !== undefined ) {
geometry.setAttribute( 'uv2', geometry.attributes.uv );
}
mesh.material = material;
}
@@ -3313,7 +3314,7 @@ class GLTFParser {
if ( metallicRoughness.baseColorTexture !== undefined ) {
pending.push( parser.assignTexture( materialParams, 'map', metallicRoughness.baseColorTexture, sRGBEncoding ) );
pending.push( parser.assignTexture( materialParams, 'map', metallicRoughness.baseColorTexture, SRGBColorSpace ) );
}
@@ -3404,7 +3405,7 @@ class GLTFParser {
if ( materialDef.emissiveTexture !== undefined && materialType !== MeshBasicMaterial ) {
pending.push( parser.assignTexture( materialParams, 'emissiveMap', materialDef.emissiveTexture, sRGBEncoding ) );
pending.push( parser.assignTexture( materialParams, 'emissiveMap', materialDef.emissiveTexture, SRGBColorSpace ) );
}
@@ -3775,6 +3776,7 @@ class GLTFParser {
const json = this.json;
const animationDef = json.animations[ animationIndex ];
const animationName = animationDef.name ? animationDef.name : 'animation_' + animationIndex;
const pendingNodes = [];
const pendingInputAccessors = [];
@@ -3791,6 +3793,8 @@ class GLTFParser {
const input = animationDef.parameters !== undefined ? animationDef.parameters[ sampler.input ] : sampler.input;
const output = animationDef.parameters !== undefined ? animationDef.parameters[ sampler.output ] : sampler.output;
if ( target.node === undefined ) continue;
pendingNodes.push( this.getDependency( 'node', name ) );
pendingInputAccessors.push( this.getDependency( 'accessor', input ) );
pendingOutputAccessors.push( this.getDependency( 'accessor', output ) );
@@ -3928,9 +3932,7 @@ class GLTFParser {
}
const name = animationDef.name ? animationDef.name : 'animation_' + animationIndex;
return new AnimationClip( name, undefined, tracks );
return new AnimationClip( animationName, undefined, tracks );
} );

View File

@@ -14,12 +14,6 @@ import {
Vector3,
} from 'three';
function computeTangents() { // @deprecated, r140
throw new Error( 'BufferGeometryUtils: computeTangents renamed to computeMikkTSpaceTangents.' );
}
function computeMikkTSpaceTangents( geometry, MikkTSpace, negateSign = true ) {
if ( ! MikkTSpace || ! MikkTSpace.isReady ) {
@@ -38,9 +32,9 @@ function computeMikkTSpaceTangents( geometry, MikkTSpace, negateSign = true ) {
if ( attribute.normalized || attribute.isInterleavedBufferAttribute ) {
const dstArray = new Float32Array( attribute.getCount() * attribute.itemSize );
const dstArray = new Float32Array( attribute.count * attribute.itemSize );
for ( let i = 0, j = 0; i < attribute.getCount(); i ++ ) {
for ( let i = 0, j = 0; i < attribute.count; i ++ ) {
dstArray[ j ++ ] = attribute.getX( i );
dstArray[ j ++ ] = attribute.getY( i );
@@ -113,7 +107,7 @@ function computeMikkTSpaceTangents( geometry, MikkTSpace, negateSign = true ) {
* @param {Boolean} useGroups
* @return {BufferGeometry}
*/
function mergeBufferGeometries( geometries, useGroups = false ) {
function mergeGeometries( geometries, useGroups = false ) {
const isIndexed = geometries[ 0 ].index !== null;
@@ -138,7 +132,7 @@ function mergeBufferGeometries( geometries, useGroups = false ) {
if ( isIndexed !== ( geometry.index !== null ) ) {
console.error( 'THREE.BufferGeometryUtils: .mergeBufferGeometries() failed with geometry at index ' + i + '. All geometries must have compatible attributes; make sure index attribute exists among all geometries, or in none of them.' );
console.error( 'THREE.BufferGeometryUtils: .mergeGeometries() failed with geometry at index ' + i + '. All geometries must have compatible attributes; make sure index attribute exists among all geometries, or in none of them.' );
return null;
}
@@ -149,7 +143,7 @@ function mergeBufferGeometries( geometries, useGroups = false ) {
if ( ! attributesUsed.has( name ) ) {
console.error( 'THREE.BufferGeometryUtils: .mergeBufferGeometries() failed with geometry at index ' + i + '. All geometries must have compatible attributes; make sure "' + name + '" attribute exists among all geometries, or in none of them.' );
console.error( 'THREE.BufferGeometryUtils: .mergeGeometries() failed with geometry at index ' + i + '. All geometries must have compatible attributes; make sure "' + name + '" attribute exists among all geometries, or in none of them.' );
return null;
}
@@ -166,7 +160,7 @@ function mergeBufferGeometries( geometries, useGroups = false ) {
if ( attributesCount !== attributesUsed.size ) {
console.error( 'THREE.BufferGeometryUtils: .mergeBufferGeometries() failed with geometry at index ' + i + '. Make sure all geometries have the same number of attributes.' );
console.error( 'THREE.BufferGeometryUtils: .mergeGeometries() failed with geometry at index ' + i + '. Make sure all geometries have the same number of attributes.' );
return null;
}
@@ -175,7 +169,7 @@ function mergeBufferGeometries( geometries, useGroups = false ) {
if ( morphTargetsRelative !== geometry.morphTargetsRelative ) {
console.error( 'THREE.BufferGeometryUtils: .mergeBufferGeometries() failed with geometry at index ' + i + '. .morphTargetsRelative must be consistent throughout all geometries.' );
console.error( 'THREE.BufferGeometryUtils: .mergeGeometries() failed with geometry at index ' + i + '. .morphTargetsRelative must be consistent throughout all geometries.' );
return null;
}
@@ -184,7 +178,7 @@ function mergeBufferGeometries( geometries, useGroups = false ) {
if ( ! morphAttributesUsed.has( name ) ) {
console.error( 'THREE.BufferGeometryUtils: .mergeBufferGeometries() failed with geometry at index ' + i + '. .morphAttributes must be consistent throughout all geometries.' );
console.error( 'THREE.BufferGeometryUtils: .mergeGeometries() failed with geometry at index ' + i + '. .morphAttributes must be consistent throughout all geometries.' );
return null;
}
@@ -209,7 +203,7 @@ function mergeBufferGeometries( geometries, useGroups = false ) {
} else {
console.error( 'THREE.BufferGeometryUtils: .mergeBufferGeometries() failed with geometry at index ' + i + '. The geometry must have either an index or a position attribute' );
console.error( 'THREE.BufferGeometryUtils: .mergeGeometries() failed with geometry at index ' + i + '. The geometry must have either an index or a position attribute' );
return null;
}
@@ -251,11 +245,11 @@ function mergeBufferGeometries( geometries, useGroups = false ) {
for ( const name in attributes ) {
const mergedAttribute = mergeBufferAttributes( attributes[ name ] );
const mergedAttribute = mergeAttributes( attributes[ name ] );
if ( ! mergedAttribute ) {
console.error( 'THREE.BufferGeometryUtils: .mergeBufferGeometries() failed while trying to merge the ' + name + ' attribute.' );
console.error( 'THREE.BufferGeometryUtils: .mergeGeometries() failed while trying to merge the ' + name + ' attribute.' );
return null;
}
@@ -285,11 +279,11 @@ function mergeBufferGeometries( geometries, useGroups = false ) {
}
const mergedMorphAttribute = mergeBufferAttributes( morphAttributesToMerge );
const mergedMorphAttribute = mergeAttributes( morphAttributesToMerge );
if ( ! mergedMorphAttribute ) {
console.error( 'THREE.BufferGeometryUtils: .mergeBufferGeometries() failed while trying to merge the ' + name + ' morphAttribute.' );
console.error( 'THREE.BufferGeometryUtils: .mergeGeometries() failed while trying to merge the ' + name + ' morphAttribute.' );
return null;
}
@@ -308,7 +302,7 @@ function mergeBufferGeometries( geometries, useGroups = false ) {
* @param {Array<BufferAttribute>} attributes
* @return {BufferAttribute}
*/
function mergeBufferAttributes( attributes ) {
function mergeAttributes( attributes ) {
let TypedArray;
let itemSize;
@@ -321,7 +315,7 @@ function mergeBufferAttributes( attributes ) {
if ( attribute.isInterleavedBufferAttribute ) {
console.error( 'THREE.BufferGeometryUtils: .mergeBufferAttributes() failed. InterleavedBufferAttributes are not supported.' );
console.error( 'THREE.BufferGeometryUtils: .mergeAttributes() failed. InterleavedBufferAttributes are not supported.' );
return null;
}
@@ -329,7 +323,7 @@ function mergeBufferAttributes( attributes ) {
if ( TypedArray === undefined ) TypedArray = attribute.array.constructor;
if ( TypedArray !== attribute.array.constructor ) {
console.error( 'THREE.BufferGeometryUtils: .mergeBufferAttributes() failed. BufferAttribute.array must be of consistent array types across matching attributes.' );
console.error( 'THREE.BufferGeometryUtils: .mergeAttributes() failed. BufferAttribute.array must be of consistent array types across matching attributes.' );
return null;
}
@@ -337,7 +331,7 @@ function mergeBufferAttributes( attributes ) {
if ( itemSize === undefined ) itemSize = attribute.itemSize;
if ( itemSize !== attribute.itemSize ) {
console.error( 'THREE.BufferGeometryUtils: .mergeBufferAttributes() failed. BufferAttribute.itemSize must be consistent across matching attributes.' );
console.error( 'THREE.BufferGeometryUtils: .mergeAttributes() failed. BufferAttribute.itemSize must be consistent across matching attributes.' );
return null;
}
@@ -345,7 +339,7 @@ function mergeBufferAttributes( attributes ) {
if ( normalized === undefined ) normalized = attribute.normalized;
if ( normalized !== attribute.normalized ) {
console.error( 'THREE.BufferGeometryUtils: .mergeBufferAttributes() failed. BufferAttribute.normalized must be consistent across matching attributes.' );
console.error( 'THREE.BufferGeometryUtils: .mergeAttributes() failed. BufferAttribute.normalized must be consistent across matching attributes.' );
return null;
}
@@ -917,9 +911,9 @@ function computeMorphedAttributes( object ) {
if ( object.isSkinnedMesh ) {
object.boneTransform( a, _vA );
object.boneTransform( b, _vB );
object.boneTransform( c, _vC );
object.applyBoneTransform( a, _vA );
object.applyBoneTransform( b, _vB );
object.applyBoneTransform( c, _vC );
}
@@ -1327,10 +1321,25 @@ function toCreasedNormals( geometry, creaseAngle = Math.PI / 3 /* 60 degrees */
}
function mergeBufferGeometries( geometries, useGroups = false ) {
console.warn( 'THREE.BufferGeometryUtils: mergeBufferGeometries() has been renamed to mergeGeometries().' ); // @deprecated, r151
return mergeGeometries( geometries, useGroups );
}
function mergeBufferAttributes( attributes ) {
console.warn( 'THREE.BufferGeometryUtils: mergeBufferAttributes() has been renamed to mergeAttributes().' ); // @deprecated, r151
return mergeAttributes( attributes );
}
export {
computeTangents,
computeMikkTSpaceTangents,
mergeGeometries,
mergeBufferGeometries,
mergeAttributes,
mergeBufferAttributes,
interleaveAttributes,
estimateBytesUsed,

View File

@@ -0,0 +1,416 @@
// MIT License
// Copyright © 2010-2023 three.js authors
import {
AnimationClip,
AnimationMixer,
Matrix4,
Quaternion,
QuaternionKeyframeTrack,
SkeletonHelper,
Vector3,
VectorKeyframeTrack
} from 'three';
function retarget( target, source, options = {} ) {
const pos = new Vector3(),
quat = new Quaternion(),
scale = new Vector3(),
bindBoneMatrix = new Matrix4(),
relativeMatrix = new Matrix4(),
globalMatrix = new Matrix4();
options.preserveMatrix = options.preserveMatrix !== undefined ? options.preserveMatrix : true;
options.preservePosition = options.preservePosition !== undefined ? options.preservePosition : true;
options.preserveHipPosition = options.preserveHipPosition !== undefined ? options.preserveHipPosition : false;
options.useTargetMatrix = options.useTargetMatrix !== undefined ? options.useTargetMatrix : false;
options.hip = options.hip !== undefined ? options.hip : 'hip';
options.names = options.names || {};
const sourceBones = source.isObject3D ? source.skeleton.bones : getBones( source ),
bones = target.isObject3D ? target.skeleton.bones : getBones( target );
let bindBones,
bone, name, boneTo,
bonesPosition;
// reset bones
if ( target.isObject3D ) {
target.skeleton.pose();
} else {
options.useTargetMatrix = true;
options.preserveMatrix = false;
}
if ( options.preservePosition ) {
bonesPosition = [];
for ( let i = 0; i < bones.length; i ++ ) {
bonesPosition.push( bones[ i ].position.clone() );
}
}
if ( options.preserveMatrix ) {
// reset matrix
target.updateMatrixWorld();
target.matrixWorld.identity();
// reset children matrix
for ( let i = 0; i < target.children.length; ++ i ) {
target.children[ i ].updateMatrixWorld( true );
}
}
if ( options.offsets ) {
bindBones = [];
for ( let i = 0; i < bones.length; ++ i ) {
bone = bones[ i ];
name = options.names[ bone.name ] || bone.name;
if ( options.offsets[ name ] ) {
bone.matrix.multiply( options.offsets[ name ] );
bone.matrix.decompose( bone.position, bone.quaternion, bone.scale );
bone.updateMatrixWorld();
}
bindBones.push( bone.matrixWorld.clone() );
}
}
for ( let i = 0; i < bones.length; ++ i ) {
bone = bones[ i ];
name = options.names[ bone.name ] || bone.name;
boneTo = getBoneByName( name, sourceBones );
globalMatrix.copy( bone.matrixWorld );
if ( boneTo ) {
boneTo.updateMatrixWorld();
if ( options.useTargetMatrix ) {
relativeMatrix.copy( boneTo.matrixWorld );
} else {
relativeMatrix.copy( target.matrixWorld ).invert();
relativeMatrix.multiply( boneTo.matrixWorld );
}
// ignore scale to extract rotation
scale.setFromMatrixScale( relativeMatrix );
relativeMatrix.scale( scale.set( 1 / scale.x, 1 / scale.y, 1 / scale.z ) );
// apply to global matrix
globalMatrix.makeRotationFromQuaternion( quat.setFromRotationMatrix( relativeMatrix ) );
if ( target.isObject3D ) {
const boneIndex = bones.indexOf( bone ),
wBindMatrix = bindBones ? bindBones[ boneIndex ] : bindBoneMatrix.copy( target.skeleton.boneInverses[ boneIndex ] ).invert();
globalMatrix.multiply( wBindMatrix );
}
globalMatrix.copyPosition( relativeMatrix );
}
if ( bone.parent && bone.parent.isBone ) {
bone.matrix.copy( bone.parent.matrixWorld ).invert();
bone.matrix.multiply( globalMatrix );
} else {
bone.matrix.copy( globalMatrix );
}
if ( options.preserveHipPosition && name === options.hip ) {
bone.matrix.setPosition( pos.set( 0, bone.position.y, 0 ) );
}
bone.matrix.decompose( bone.position, bone.quaternion, bone.scale );
bone.updateMatrixWorld();
}
if ( options.preservePosition ) {
for ( let i = 0; i < bones.length; ++ i ) {
bone = bones[ i ];
name = options.names[ bone.name ] || bone.name;
if ( name !== options.hip ) {
bone.position.copy( bonesPosition[ i ] );
}
}
}
if ( options.preserveMatrix ) {
// restore matrix
target.updateMatrixWorld( true );
}
}
function retargetClip( target, source, clip, options = {} ) {
options.useFirstFramePosition = options.useFirstFramePosition !== undefined ? options.useFirstFramePosition : false;
options.fps = options.fps !== undefined ? options.fps : 30;
options.names = options.names || [];
if ( ! source.isObject3D ) {
source = getHelperFromSkeleton( source );
}
const numFrames = Math.round( clip.duration * ( options.fps / 1000 ) * 1000 ),
delta = 1 / options.fps,
convertedTracks = [],
mixer = new AnimationMixer( source ),
bones = getBones( target.skeleton ),
boneDatas = [];
let positionOffset,
bone, boneTo, boneData,
name;
mixer.clipAction( clip ).play();
mixer.update( 0 );
source.updateMatrixWorld();
for ( let i = 0; i < numFrames; ++ i ) {
const time = i * delta;
retarget( target, source, options );
for ( let j = 0; j < bones.length; ++ j ) {
name = options.names[ bones[ j ].name ] || bones[ j ].name;
boneTo = getBoneByName( name, source.skeleton );
if ( boneTo ) {
bone = bones[ j ];
boneData = boneDatas[ j ] = boneDatas[ j ] || { bone: bone };
if ( options.hip === name ) {
if ( ! boneData.pos ) {
boneData.pos = {
times: new Float32Array( numFrames ),
values: new Float32Array( numFrames * 3 )
};
}
if ( options.useFirstFramePosition ) {
if ( i === 0 ) {
positionOffset = bone.position.clone();
}
bone.position.sub( positionOffset );
}
boneData.pos.times[ i ] = time;
bone.position.toArray( boneData.pos.values, i * 3 );
}
if ( ! boneData.quat ) {
boneData.quat = {
times: new Float32Array( numFrames ),
values: new Float32Array( numFrames * 4 )
};
}
boneData.quat.times[ i ] = time;
bone.quaternion.toArray( boneData.quat.values, i * 4 );
}
}
mixer.update( delta );
source.updateMatrixWorld();
}
for ( let i = 0; i < boneDatas.length; ++ i ) {
boneData = boneDatas[ i ];
if ( boneData ) {
if ( boneData.pos ) {
convertedTracks.push( new VectorKeyframeTrack(
'.bones[' + boneData.bone.name + '].position',
boneData.pos.times,
boneData.pos.values
) );
}
convertedTracks.push( new QuaternionKeyframeTrack(
'.bones[' + boneData.bone.name + '].quaternion',
boneData.quat.times,
boneData.quat.values
) );
}
}
mixer.uncacheAction( clip );
return new AnimationClip( clip.name, - 1, convertedTracks );
}
function clone( source ) {
const sourceLookup = new Map();
const cloneLookup = new Map();
const clone = source.clone();
parallelTraverse( source, clone, function ( sourceNode, clonedNode ) {
sourceLookup.set( clonedNode, sourceNode );
cloneLookup.set( sourceNode, clonedNode );
} );
clone.traverse( function ( node ) {
if ( ! node.isSkinnedMesh ) return;
const clonedMesh = node;
const sourceMesh = sourceLookup.get( node );
const sourceBones = sourceMesh.skeleton.bones;
clonedMesh.skeleton = sourceMesh.skeleton.clone();
clonedMesh.bindMatrix.copy( sourceMesh.bindMatrix );
clonedMesh.skeleton.bones = sourceBones.map( function ( bone ) {
return cloneLookup.get( bone );
} );
clonedMesh.bind( clonedMesh.skeleton, clonedMesh.bindMatrix );
} );
return clone;
}
// internal helper
function getBoneByName( name, skeleton ) {
for ( let i = 0, bones = getBones( skeleton ); i < bones.length; i ++ ) {
if ( name === bones[ i ].name )
return bones[ i ];
}
}
function getBones( skeleton ) {
return Array.isArray( skeleton ) ? skeleton : skeleton.bones;
}
function getHelperFromSkeleton( skeleton ) {
const source = new SkeletonHelper( skeleton.bones[ 0 ] );
source.skeleton = skeleton;
return source;
}
function parallelTraverse( a, b, callback ) {
callback( a, b );
for ( let i = 0; i < a.children.length; i ++ ) {
parallelTraverse( a.children[ i ], b.children[ i ], callback );
}
}
export {
retarget,
retargetClip,
clone,
};

View File

@@ -7,3 +7,8 @@ export {
GLTFLoader,
} from "./examples/jsm/loaders/GLTFLoader";
export {
DRACOLoader,
} from "./examples/jsm/loaders/DRACOLoader";
export * as SkeletonUtils from "./examples/jsm/utils/SkeletonUtils";

File diff suppressed because it is too large Load Diff

View File

@@ -40,7 +40,7 @@
"@material-ui/icons": "4.9.1",
"@material-ui/lab": "4.0.0-alpha.56",
"@supercharge/promise-pool": "^1.6.0",
"algoliasearch": "^3.33.0",
"algoliasearch": "3.33.0",
"axios": "^0.18.1",
"blueimp-md5": "^2.10.0",
"classnames": "2.2.5",
@@ -69,7 +69,7 @@
"react-error-boundary": "^1.2.0",
"react-instantsearch-hooks": "^6.41.0",
"react-json-view": "^1.16.1",
"react-markdown": "^6.0.3",
"react-markdown": "^8.0.6",
"react-measure": "2.3.0",
"react-monaco-editor": "^0.18.0",
"react-mosaic-component": "github:4ian/react-mosaic#v3.1.0",
@@ -79,10 +79,12 @@
"react-test-renderer": "16.14.0",
"react-virtualized": "9.21.1",
"recharts": "^2.1.10",
"remark-gfm": "^3.0.1",
"remark-parse": "^10.0.2",
"semver": "7.0.0",
"slugs": "0.1.3",
"source-map-explorer": "^2.0.1",
"three": "0.151.2",
"three": "0.152.0",
"url-search-params": "^1.0.2",
"wait-promise": "0.4.1",
"xxhashjs": "^0.2.2"
@@ -165,5 +167,10 @@
"not dead",
"not ie <= 11",
"not op_mini all"
]
}
],
"jest": {
"transformIgnorePatterns": [
"<rootDir>/node_modules/(?!react-markdown|unified|remark-parse|mdast-util-from-markdown|micromark|decode-named-character-reference|remark-rehype|trim-lines|hast-util-whitespace|remark-gfm|mdast-util-gfm|mdast-util-find-and-replace|mdast-util-to-markdown|markdown-table)"
]
}
}

Binary file not shown.

View File

@@ -0,0 +1,116 @@
var $jscomp=$jscomp||{};$jscomp.scope={};$jscomp.arrayIteratorImpl=function(h){var n=0;return function(){return n<h.length?{done:!1,value:h[n++]}:{done:!0}}};$jscomp.arrayIterator=function(h){return{next:$jscomp.arrayIteratorImpl(h)}};$jscomp.makeIterator=function(h){var n="undefined"!=typeof Symbol&&Symbol.iterator&&h[Symbol.iterator];return n?n.call(h):$jscomp.arrayIterator(h)};$jscomp.ASSUME_ES5=!1;$jscomp.ASSUME_NO_NATIVE_MAP=!1;$jscomp.ASSUME_NO_NATIVE_SET=!1;$jscomp.SIMPLE_FROUND_POLYFILL=!1;
$jscomp.ISOLATE_POLYFILLS=!1;$jscomp.FORCE_POLYFILL_PROMISE=!1;$jscomp.FORCE_POLYFILL_PROMISE_WHEN_NO_UNHANDLED_REJECTION=!1;$jscomp.getGlobal=function(h){h=["object"==typeof globalThis&&globalThis,h,"object"==typeof window&&window,"object"==typeof self&&self,"object"==typeof global&&global];for(var n=0;n<h.length;++n){var k=h[n];if(k&&k.Math==Math)return k}throw Error("Cannot find global object");};$jscomp.global=$jscomp.getGlobal(this);
$jscomp.defineProperty=$jscomp.ASSUME_ES5||"function"==typeof Object.defineProperties?Object.defineProperty:function(h,n,k){if(h==Array.prototype||h==Object.prototype)return h;h[n]=k.value;return h};$jscomp.IS_SYMBOL_NATIVE="function"===typeof Symbol&&"symbol"===typeof Symbol("x");$jscomp.TRUST_ES6_POLYFILLS=!$jscomp.ISOLATE_POLYFILLS||$jscomp.IS_SYMBOL_NATIVE;$jscomp.polyfills={};$jscomp.propertyToPolyfillSymbol={};$jscomp.POLYFILL_PREFIX="$jscp$";
var $jscomp$lookupPolyfilledValue=function(h,n){var k=$jscomp.propertyToPolyfillSymbol[n];if(null==k)return h[n];k=h[k];return void 0!==k?k:h[n]};$jscomp.polyfill=function(h,n,k,p){n&&($jscomp.ISOLATE_POLYFILLS?$jscomp.polyfillIsolated(h,n,k,p):$jscomp.polyfillUnisolated(h,n,k,p))};
$jscomp.polyfillUnisolated=function(h,n,k,p){k=$jscomp.global;h=h.split(".");for(p=0;p<h.length-1;p++){var l=h[p];if(!(l in k))return;k=k[l]}h=h[h.length-1];p=k[h];n=n(p);n!=p&&null!=n&&$jscomp.defineProperty(k,h,{configurable:!0,writable:!0,value:n})};
$jscomp.polyfillIsolated=function(h,n,k,p){var l=h.split(".");h=1===l.length;p=l[0];p=!h&&p in $jscomp.polyfills?$jscomp.polyfills:$jscomp.global;for(var y=0;y<l.length-1;y++){var f=l[y];if(!(f in p))return;p=p[f]}l=l[l.length-1];k=$jscomp.IS_SYMBOL_NATIVE&&"es6"===k?p[l]:null;n=n(k);null!=n&&(h?$jscomp.defineProperty($jscomp.polyfills,l,{configurable:!0,writable:!0,value:n}):n!==k&&(void 0===$jscomp.propertyToPolyfillSymbol[l]&&(k=1E9*Math.random()>>>0,$jscomp.propertyToPolyfillSymbol[l]=$jscomp.IS_SYMBOL_NATIVE?
$jscomp.global.Symbol(l):$jscomp.POLYFILL_PREFIX+k+"$"+l),$jscomp.defineProperty(p,$jscomp.propertyToPolyfillSymbol[l],{configurable:!0,writable:!0,value:n})))};
$jscomp.polyfill("Promise",function(h){function n(){this.batch_=null}function k(f){return f instanceof l?f:new l(function(q,u){q(f)})}if(h&&(!($jscomp.FORCE_POLYFILL_PROMISE||$jscomp.FORCE_POLYFILL_PROMISE_WHEN_NO_UNHANDLED_REJECTION&&"undefined"===typeof $jscomp.global.PromiseRejectionEvent)||!$jscomp.global.Promise||-1===$jscomp.global.Promise.toString().indexOf("[native code]")))return h;n.prototype.asyncExecute=function(f){if(null==this.batch_){this.batch_=[];var q=this;this.asyncExecuteFunction(function(){q.executeBatch_()})}this.batch_.push(f)};
var p=$jscomp.global.setTimeout;n.prototype.asyncExecuteFunction=function(f){p(f,0)};n.prototype.executeBatch_=function(){for(;this.batch_&&this.batch_.length;){var f=this.batch_;this.batch_=[];for(var q=0;q<f.length;++q){var u=f[q];f[q]=null;try{u()}catch(A){this.asyncThrow_(A)}}}this.batch_=null};n.prototype.asyncThrow_=function(f){this.asyncExecuteFunction(function(){throw f;})};var l=function(f){this.state_=0;this.result_=void 0;this.onSettledCallbacks_=[];this.isRejectionHandled_=!1;var q=this.createResolveAndReject_();
try{f(q.resolve,q.reject)}catch(u){q.reject(u)}};l.prototype.createResolveAndReject_=function(){function f(A){return function(F){u||(u=!0,A.call(q,F))}}var q=this,u=!1;return{resolve:f(this.resolveTo_),reject:f(this.reject_)}};l.prototype.resolveTo_=function(f){if(f===this)this.reject_(new TypeError("A Promise cannot resolve to itself"));else if(f instanceof l)this.settleSameAsPromise_(f);else{a:switch(typeof f){case "object":var q=null!=f;break a;case "function":q=!0;break a;default:q=!1}q?this.resolveToNonPromiseObj_(f):
this.fulfill_(f)}};l.prototype.resolveToNonPromiseObj_=function(f){var q=void 0;try{q=f.then}catch(u){this.reject_(u);return}"function"==typeof q?this.settleSameAsThenable_(q,f):this.fulfill_(f)};l.prototype.reject_=function(f){this.settle_(2,f)};l.prototype.fulfill_=function(f){this.settle_(1,f)};l.prototype.settle_=function(f,q){if(0!=this.state_)throw Error("Cannot settle("+f+", "+q+"): Promise already settled in state"+this.state_);this.state_=f;this.result_=q;2===this.state_&&this.scheduleUnhandledRejectionCheck_();
this.executeOnSettledCallbacks_()};l.prototype.scheduleUnhandledRejectionCheck_=function(){var f=this;p(function(){if(f.notifyUnhandledRejection_()){var q=$jscomp.global.console;"undefined"!==typeof q&&q.error(f.result_)}},1)};l.prototype.notifyUnhandledRejection_=function(){if(this.isRejectionHandled_)return!1;var f=$jscomp.global.CustomEvent,q=$jscomp.global.Event,u=$jscomp.global.dispatchEvent;if("undefined"===typeof u)return!0;"function"===typeof f?f=new f("unhandledrejection",{cancelable:!0}):
"function"===typeof q?f=new q("unhandledrejection",{cancelable:!0}):(f=$jscomp.global.document.createEvent("CustomEvent"),f.initCustomEvent("unhandledrejection",!1,!0,f));f.promise=this;f.reason=this.result_;return u(f)};l.prototype.executeOnSettledCallbacks_=function(){if(null!=this.onSettledCallbacks_){for(var f=0;f<this.onSettledCallbacks_.length;++f)y.asyncExecute(this.onSettledCallbacks_[f]);this.onSettledCallbacks_=null}};var y=new n;l.prototype.settleSameAsPromise_=function(f){var q=this.createResolveAndReject_();
f.callWhenSettled_(q.resolve,q.reject)};l.prototype.settleSameAsThenable_=function(f,q){var u=this.createResolveAndReject_();try{f.call(q,u.resolve,u.reject)}catch(A){u.reject(A)}};l.prototype.then=function(f,q){function u(w,B){return"function"==typeof w?function(R){try{A(w(R))}catch(Z){F(Z)}}:B}var A,F,v=new l(function(w,B){A=w;F=B});this.callWhenSettled_(u(f,A),u(q,F));return v};l.prototype.catch=function(f){return this.then(void 0,f)};l.prototype.callWhenSettled_=function(f,q){function u(){switch(A.state_){case 1:f(A.result_);
break;case 2:q(A.result_);break;default:throw Error("Unexpected state: "+A.state_);}}var A=this;null==this.onSettledCallbacks_?y.asyncExecute(u):this.onSettledCallbacks_.push(u);this.isRejectionHandled_=!0};l.resolve=k;l.reject=function(f){return new l(function(q,u){u(f)})};l.race=function(f){return new l(function(q,u){for(var A=$jscomp.makeIterator(f),F=A.next();!F.done;F=A.next())k(F.value).callWhenSettled_(q,u)})};l.all=function(f){var q=$jscomp.makeIterator(f),u=q.next();return u.done?k([]):new l(function(A,
F){function v(R){return function(Z){w[R]=Z;B--;0==B&&A(w)}}var w=[],B=0;do w.push(void 0),B++,k(u.value).callWhenSettled_(v(w.length-1),F),u=q.next();while(!u.done)})};return l},"es6","es3");$jscomp.owns=function(h,n){return Object.prototype.hasOwnProperty.call(h,n)};$jscomp.assign=$jscomp.TRUST_ES6_POLYFILLS&&"function"==typeof Object.assign?Object.assign:function(h,n){for(var k=1;k<arguments.length;k++){var p=arguments[k];if(p)for(var l in p)$jscomp.owns(p,l)&&(h[l]=p[l])}return h};
$jscomp.polyfill("Object.assign",function(h){return h||$jscomp.assign},"es6","es3");$jscomp.checkStringArgs=function(h,n,k){if(null==h)throw new TypeError("The 'this' value for String.prototype."+k+" must not be null or undefined");if(n instanceof RegExp)throw new TypeError("First argument to String.prototype."+k+" must not be a regular expression");return h+""};
$jscomp.polyfill("String.prototype.startsWith",function(h){return h?h:function(n,k){var p=$jscomp.checkStringArgs(this,n,"startsWith");n+="";var l=p.length,y=n.length;k=Math.max(0,Math.min(k|0,p.length));for(var f=0;f<y&&k<l;)if(p[k++]!=n[f++])return!1;return f>=y}},"es6","es3");
$jscomp.polyfill("Array.prototype.copyWithin",function(h){function n(k){k=Number(k);return Infinity===k||-Infinity===k?k:k|0}return h?h:function(k,p,l){var y=this.length;k=n(k);p=n(p);l=void 0===l?y:n(l);k=0>k?Math.max(y+k,0):Math.min(k,y);p=0>p?Math.max(y+p,0):Math.min(p,y);l=0>l?Math.max(y+l,0):Math.min(l,y);if(k<p)for(;p<l;)p in this?this[k++]=this[p++]:(delete this[k++],p++);else for(l=Math.min(l,y+p-k),k+=l-p;l>p;)--l in this?this[--k]=this[l]:delete this[--k];return this}},"es6","es3");
$jscomp.typedArrayCopyWithin=function(h){return h?h:Array.prototype.copyWithin};$jscomp.polyfill("Int8Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Uint8Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Uint8ClampedArray.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Int16Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");
$jscomp.polyfill("Uint16Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Int32Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Uint32Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Float32Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Float64Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");
var DracoDecoderModule=function(){var h="undefined"!==typeof document&&document.currentScript?document.currentScript.src:void 0;"undefined"!==typeof __filename&&(h=h||__filename);return function(n){function k(e){return a.locateFile?a.locateFile(e,U):U+e}function p(e,b){if(e){var c=ia;var d=e+b;for(b=e;c[b]&&!(b>=d);)++b;if(16<b-e&&c.buffer&&ra)c=ra.decode(c.subarray(e,b));else{for(d="";e<b;){var g=c[e++];if(g&128){var t=c[e++]&63;if(192==(g&224))d+=String.fromCharCode((g&31)<<6|t);else{var aa=c[e++]&
63;g=224==(g&240)?(g&15)<<12|t<<6|aa:(g&7)<<18|t<<12|aa<<6|c[e++]&63;65536>g?d+=String.fromCharCode(g):(g-=65536,d+=String.fromCharCode(55296|g>>10,56320|g&1023))}}else d+=String.fromCharCode(g)}c=d}}else c="";return c}function l(){var e=ja.buffer;a.HEAP8=W=new Int8Array(e);a.HEAP16=new Int16Array(e);a.HEAP32=ca=new Int32Array(e);a.HEAPU8=ia=new Uint8Array(e);a.HEAPU16=new Uint16Array(e);a.HEAPU32=Y=new Uint32Array(e);a.HEAPF32=new Float32Array(e);a.HEAPF64=new Float64Array(e)}function y(e){if(a.onAbort)a.onAbort(e);
e="Aborted("+e+")";da(e);sa=!0;e=new WebAssembly.RuntimeError(e+". Build with -sASSERTIONS for more info.");ka(e);throw e;}function f(e){try{if(e==P&&ea)return new Uint8Array(ea);if(ma)return ma(e);throw"both async and sync fetching of the wasm failed";}catch(b){y(b)}}function q(){if(!ea&&(ta||fa)){if("function"==typeof fetch&&!P.startsWith("file://"))return fetch(P,{credentials:"same-origin"}).then(function(e){if(!e.ok)throw"failed to load wasm binary file at '"+P+"'";return e.arrayBuffer()}).catch(function(){return f(P)});
if(na)return new Promise(function(e,b){na(P,function(c){e(new Uint8Array(c))},b)})}return Promise.resolve().then(function(){return f(P)})}function u(e){for(;0<e.length;)e.shift()(a)}function A(e){this.excPtr=e;this.ptr=e-24;this.set_type=function(b){Y[this.ptr+4>>2]=b};this.get_type=function(){return Y[this.ptr+4>>2]};this.set_destructor=function(b){Y[this.ptr+8>>2]=b};this.get_destructor=function(){return Y[this.ptr+8>>2]};this.set_refcount=function(b){ca[this.ptr>>2]=b};this.set_caught=function(b){W[this.ptr+
12>>0]=b?1:0};this.get_caught=function(){return 0!=W[this.ptr+12>>0]};this.set_rethrown=function(b){W[this.ptr+13>>0]=b?1:0};this.get_rethrown=function(){return 0!=W[this.ptr+13>>0]};this.init=function(b,c){this.set_adjusted_ptr(0);this.set_type(b);this.set_destructor(c);this.set_refcount(0);this.set_caught(!1);this.set_rethrown(!1)};this.add_ref=function(){ca[this.ptr>>2]+=1};this.release_ref=function(){var b=ca[this.ptr>>2];ca[this.ptr>>2]=b-1;return 1===b};this.set_adjusted_ptr=function(b){Y[this.ptr+
16>>2]=b};this.get_adjusted_ptr=function(){return Y[this.ptr+16>>2]};this.get_exception_ptr=function(){if(ua(this.get_type()))return Y[this.excPtr>>2];var b=this.get_adjusted_ptr();return 0!==b?b:this.excPtr}}function F(){function e(){if(!la&&(la=!0,a.calledRun=!0,!sa)){va=!0;u(oa);wa(a);if(a.onRuntimeInitialized)a.onRuntimeInitialized();if(a.postRun)for("function"==typeof a.postRun&&(a.postRun=[a.postRun]);a.postRun.length;)xa.unshift(a.postRun.shift());u(xa)}}if(!(0<ba)){if(a.preRun)for("function"==
typeof a.preRun&&(a.preRun=[a.preRun]);a.preRun.length;)ya.unshift(a.preRun.shift());u(ya);0<ba||(a.setStatus?(a.setStatus("Running..."),setTimeout(function(){setTimeout(function(){a.setStatus("")},1);e()},1)):e())}}function v(){}function w(e){return(e||v).__cache__}function B(e,b){var c=w(b),d=c[e];if(d)return d;d=Object.create((b||v).prototype);d.ptr=e;return c[e]=d}function R(e){if("string"===typeof e){for(var b=0,c=0;c<e.length;++c){var d=e.charCodeAt(c);127>=d?b++:2047>=d?b+=2:55296<=d&&57343>=
d?(b+=4,++c):b+=3}b=Array(b+1);c=0;d=b.length;if(0<d){d=c+d-1;for(var g=0;g<e.length;++g){var t=e.charCodeAt(g);if(55296<=t&&57343>=t){var aa=e.charCodeAt(++g);t=65536+((t&1023)<<10)|aa&1023}if(127>=t){if(c>=d)break;b[c++]=t}else{if(2047>=t){if(c+1>=d)break;b[c++]=192|t>>6}else{if(65535>=t){if(c+2>=d)break;b[c++]=224|t>>12}else{if(c+3>=d)break;b[c++]=240|t>>18;b[c++]=128|t>>12&63}b[c++]=128|t>>6&63}b[c++]=128|t&63}}b[c]=0}e=r.alloc(b,W);r.copy(b,W,e);return e}return e}function Z(e){if("object"===
typeof e){var b=r.alloc(e,W);r.copy(e,W,b);return b}return e}function X(){throw"cannot construct a VoidPtr, no constructor in IDL";}function S(){this.ptr=za();w(S)[this.ptr]=this}function Q(){this.ptr=Aa();w(Q)[this.ptr]=this}function V(){this.ptr=Ba();w(V)[this.ptr]=this}function x(){this.ptr=Ca();w(x)[this.ptr]=this}function D(){this.ptr=Da();w(D)[this.ptr]=this}function G(){this.ptr=Ea();w(G)[this.ptr]=this}function H(){this.ptr=Fa();w(H)[this.ptr]=this}function E(){this.ptr=Ga();w(E)[this.ptr]=
this}function T(){this.ptr=Ha();w(T)[this.ptr]=this}function C(){throw"cannot construct a Status, no constructor in IDL";}function I(){this.ptr=Ia();w(I)[this.ptr]=this}function J(){this.ptr=Ja();w(J)[this.ptr]=this}function K(){this.ptr=Ka();w(K)[this.ptr]=this}function L(){this.ptr=La();w(L)[this.ptr]=this}function M(){this.ptr=Ma();w(M)[this.ptr]=this}function N(){this.ptr=Na();w(N)[this.ptr]=this}function O(){this.ptr=Oa();w(O)[this.ptr]=this}function z(){this.ptr=Pa();w(z)[this.ptr]=this}function m(){this.ptr=
Qa();w(m)[this.ptr]=this}n=void 0===n?{}:n;var a="undefined"!=typeof n?n:{},wa,ka;a.ready=new Promise(function(e,b){wa=e;ka=b});var Ra=!1,Sa=!1;a.onRuntimeInitialized=function(){Ra=!0;if(Sa&&"function"===typeof a.onModuleLoaded)a.onModuleLoaded(a)};a.onModuleParsed=function(){Sa=!0;if(Ra&&"function"===typeof a.onModuleLoaded)a.onModuleLoaded(a)};a.isVersionSupported=function(e){if("string"!==typeof e)return!1;e=e.split(".");return 2>e.length||3<e.length?!1:1==e[0]&&0<=e[1]&&5>=e[1]?!0:0!=e[0]||10<
e[1]?!1:!0};var Ta=Object.assign({},a),ta="object"==typeof window,fa="function"==typeof importScripts,Ua="object"==typeof process&&"object"==typeof process.versions&&"string"==typeof process.versions.node,U="";if(Ua){var Va=require("fs"),pa=require("path");U=fa?pa.dirname(U)+"/":__dirname+"/";var Wa=function(e,b){e=e.startsWith("file://")?new URL(e):pa.normalize(e);return Va.readFileSync(e,b?void 0:"utf8")};var ma=function(e){e=Wa(e,!0);e.buffer||(e=new Uint8Array(e));return e};var na=function(e,
b,c){e=e.startsWith("file://")?new URL(e):pa.normalize(e);Va.readFile(e,function(d,g){d?c(d):b(g.buffer)})};1<process.argv.length&&process.argv[1].replace(/\\/g,"/");process.argv.slice(2);a.inspect=function(){return"[Emscripten Module object]"}}else if(ta||fa)fa?U=self.location.href:"undefined"!=typeof document&&document.currentScript&&(U=document.currentScript.src),h&&(U=h),U=0!==U.indexOf("blob:")?U.substr(0,U.replace(/[?#].*/,"").lastIndexOf("/")+1):"",Wa=function(e){var b=new XMLHttpRequest;b.open("GET",
e,!1);b.send(null);return b.responseText},fa&&(ma=function(e){var b=new XMLHttpRequest;b.open("GET",e,!1);b.responseType="arraybuffer";b.send(null);return new Uint8Array(b.response)}),na=function(e,b,c){var d=new XMLHttpRequest;d.open("GET",e,!0);d.responseType="arraybuffer";d.onload=function(){200==d.status||0==d.status&&d.response?b(d.response):c()};d.onerror=c;d.send(null)};a.print||console.log.bind(console);var da=a.printErr||console.warn.bind(console);Object.assign(a,Ta);Ta=null;var ea;a.wasmBinary&&
(ea=a.wasmBinary);"object"!=typeof WebAssembly&&y("no native wasm support detected");var ja,sa=!1,ra="undefined"!=typeof TextDecoder?new TextDecoder("utf8"):void 0,W,ia,ca,Y,ya=[],oa=[],xa=[],va=!1,ba=0,qa=null,ha=null;var P="draco_decoder_gltf.wasm";P.startsWith("data:application/octet-stream;base64,")||(P=k(P));var pd=0,qd={b:function(e,b,c){(new A(e)).init(b,c);pd++;throw e;},a:function(){y("")},d:function(e,b,c){ia.copyWithin(e,b,b+c)},c:function(e){var b=ia.length;e>>>=0;if(2147483648<e)return!1;
for(var c=1;4>=c;c*=2){var d=b*(1+.2/c);d=Math.min(d,e+100663296);var g=Math;d=Math.max(e,d);g=g.min.call(g,2147483648,d+(65536-d%65536)%65536);a:{d=ja.buffer;try{ja.grow(g-d.byteLength+65535>>>16);l();var t=1;break a}catch(aa){}t=void 0}if(t)return!0}return!1}};(function(){function e(g,t){a.asm=g.exports;ja=a.asm.e;l();oa.unshift(a.asm.f);ba--;a.monitorRunDependencies&&a.monitorRunDependencies(ba);0==ba&&(null!==qa&&(clearInterval(qa),qa=null),ha&&(g=ha,ha=null,g()))}function b(g){e(g.instance)}
function c(g){return q().then(function(t){return WebAssembly.instantiate(t,d)}).then(function(t){return t}).then(g,function(t){da("failed to asynchronously prepare wasm: "+t);y(t)})}var d={a:qd};ba++;a.monitorRunDependencies&&a.monitorRunDependencies(ba);if(a.instantiateWasm)try{return a.instantiateWasm(d,e)}catch(g){da("Module.instantiateWasm callback failed with error: "+g),ka(g)}(function(){return ea||"function"!=typeof WebAssembly.instantiateStreaming||P.startsWith("data:application/octet-stream;base64,")||
P.startsWith("file://")||Ua||"function"!=typeof fetch?c(b):fetch(P,{credentials:"same-origin"}).then(function(g){return WebAssembly.instantiateStreaming(g,d).then(b,function(t){da("wasm streaming compile failed: "+t);da("falling back to ArrayBuffer instantiation");return c(b)})})})().catch(ka);return{}})();var Xa=a._emscripten_bind_VoidPtr___destroy___0=function(){return(Xa=a._emscripten_bind_VoidPtr___destroy___0=a.asm.h).apply(null,arguments)},za=a._emscripten_bind_DecoderBuffer_DecoderBuffer_0=
function(){return(za=a._emscripten_bind_DecoderBuffer_DecoderBuffer_0=a.asm.i).apply(null,arguments)},Ya=a._emscripten_bind_DecoderBuffer_Init_2=function(){return(Ya=a._emscripten_bind_DecoderBuffer_Init_2=a.asm.j).apply(null,arguments)},Za=a._emscripten_bind_DecoderBuffer___destroy___0=function(){return(Za=a._emscripten_bind_DecoderBuffer___destroy___0=a.asm.k).apply(null,arguments)},Aa=a._emscripten_bind_AttributeTransformData_AttributeTransformData_0=function(){return(Aa=a._emscripten_bind_AttributeTransformData_AttributeTransformData_0=
a.asm.l).apply(null,arguments)},$a=a._emscripten_bind_AttributeTransformData_transform_type_0=function(){return($a=a._emscripten_bind_AttributeTransformData_transform_type_0=a.asm.m).apply(null,arguments)},ab=a._emscripten_bind_AttributeTransformData___destroy___0=function(){return(ab=a._emscripten_bind_AttributeTransformData___destroy___0=a.asm.n).apply(null,arguments)},Ba=a._emscripten_bind_GeometryAttribute_GeometryAttribute_0=function(){return(Ba=a._emscripten_bind_GeometryAttribute_GeometryAttribute_0=
a.asm.o).apply(null,arguments)},bb=a._emscripten_bind_GeometryAttribute___destroy___0=function(){return(bb=a._emscripten_bind_GeometryAttribute___destroy___0=a.asm.p).apply(null,arguments)},Ca=a._emscripten_bind_PointAttribute_PointAttribute_0=function(){return(Ca=a._emscripten_bind_PointAttribute_PointAttribute_0=a.asm.q).apply(null,arguments)},cb=a._emscripten_bind_PointAttribute_size_0=function(){return(cb=a._emscripten_bind_PointAttribute_size_0=a.asm.r).apply(null,arguments)},db=a._emscripten_bind_PointAttribute_GetAttributeTransformData_0=
function(){return(db=a._emscripten_bind_PointAttribute_GetAttributeTransformData_0=a.asm.s).apply(null,arguments)},eb=a._emscripten_bind_PointAttribute_attribute_type_0=function(){return(eb=a._emscripten_bind_PointAttribute_attribute_type_0=a.asm.t).apply(null,arguments)},fb=a._emscripten_bind_PointAttribute_data_type_0=function(){return(fb=a._emscripten_bind_PointAttribute_data_type_0=a.asm.u).apply(null,arguments)},gb=a._emscripten_bind_PointAttribute_num_components_0=function(){return(gb=a._emscripten_bind_PointAttribute_num_components_0=
a.asm.v).apply(null,arguments)},hb=a._emscripten_bind_PointAttribute_normalized_0=function(){return(hb=a._emscripten_bind_PointAttribute_normalized_0=a.asm.w).apply(null,arguments)},ib=a._emscripten_bind_PointAttribute_byte_stride_0=function(){return(ib=a._emscripten_bind_PointAttribute_byte_stride_0=a.asm.x).apply(null,arguments)},jb=a._emscripten_bind_PointAttribute_byte_offset_0=function(){return(jb=a._emscripten_bind_PointAttribute_byte_offset_0=a.asm.y).apply(null,arguments)},kb=a._emscripten_bind_PointAttribute_unique_id_0=
function(){return(kb=a._emscripten_bind_PointAttribute_unique_id_0=a.asm.z).apply(null,arguments)},lb=a._emscripten_bind_PointAttribute___destroy___0=function(){return(lb=a._emscripten_bind_PointAttribute___destroy___0=a.asm.A).apply(null,arguments)},Da=a._emscripten_bind_AttributeQuantizationTransform_AttributeQuantizationTransform_0=function(){return(Da=a._emscripten_bind_AttributeQuantizationTransform_AttributeQuantizationTransform_0=a.asm.B).apply(null,arguments)},mb=a._emscripten_bind_AttributeQuantizationTransform_InitFromAttribute_1=
function(){return(mb=a._emscripten_bind_AttributeQuantizationTransform_InitFromAttribute_1=a.asm.C).apply(null,arguments)},nb=a._emscripten_bind_AttributeQuantizationTransform_quantization_bits_0=function(){return(nb=a._emscripten_bind_AttributeQuantizationTransform_quantization_bits_0=a.asm.D).apply(null,arguments)},ob=a._emscripten_bind_AttributeQuantizationTransform_min_value_1=function(){return(ob=a._emscripten_bind_AttributeQuantizationTransform_min_value_1=a.asm.E).apply(null,arguments)},pb=
a._emscripten_bind_AttributeQuantizationTransform_range_0=function(){return(pb=a._emscripten_bind_AttributeQuantizationTransform_range_0=a.asm.F).apply(null,arguments)},qb=a._emscripten_bind_AttributeQuantizationTransform___destroy___0=function(){return(qb=a._emscripten_bind_AttributeQuantizationTransform___destroy___0=a.asm.G).apply(null,arguments)},Ea=a._emscripten_bind_AttributeOctahedronTransform_AttributeOctahedronTransform_0=function(){return(Ea=a._emscripten_bind_AttributeOctahedronTransform_AttributeOctahedronTransform_0=
a.asm.H).apply(null,arguments)},rb=a._emscripten_bind_AttributeOctahedronTransform_InitFromAttribute_1=function(){return(rb=a._emscripten_bind_AttributeOctahedronTransform_InitFromAttribute_1=a.asm.I).apply(null,arguments)},sb=a._emscripten_bind_AttributeOctahedronTransform_quantization_bits_0=function(){return(sb=a._emscripten_bind_AttributeOctahedronTransform_quantization_bits_0=a.asm.J).apply(null,arguments)},tb=a._emscripten_bind_AttributeOctahedronTransform___destroy___0=function(){return(tb=
a._emscripten_bind_AttributeOctahedronTransform___destroy___0=a.asm.K).apply(null,arguments)},Fa=a._emscripten_bind_PointCloud_PointCloud_0=function(){return(Fa=a._emscripten_bind_PointCloud_PointCloud_0=a.asm.L).apply(null,arguments)},ub=a._emscripten_bind_PointCloud_num_attributes_0=function(){return(ub=a._emscripten_bind_PointCloud_num_attributes_0=a.asm.M).apply(null,arguments)},vb=a._emscripten_bind_PointCloud_num_points_0=function(){return(vb=a._emscripten_bind_PointCloud_num_points_0=a.asm.N).apply(null,
arguments)},wb=a._emscripten_bind_PointCloud___destroy___0=function(){return(wb=a._emscripten_bind_PointCloud___destroy___0=a.asm.O).apply(null,arguments)},Ga=a._emscripten_bind_Mesh_Mesh_0=function(){return(Ga=a._emscripten_bind_Mesh_Mesh_0=a.asm.P).apply(null,arguments)},xb=a._emscripten_bind_Mesh_num_faces_0=function(){return(xb=a._emscripten_bind_Mesh_num_faces_0=a.asm.Q).apply(null,arguments)},yb=a._emscripten_bind_Mesh_num_attributes_0=function(){return(yb=a._emscripten_bind_Mesh_num_attributes_0=
a.asm.R).apply(null,arguments)},zb=a._emscripten_bind_Mesh_num_points_0=function(){return(zb=a._emscripten_bind_Mesh_num_points_0=a.asm.S).apply(null,arguments)},Ab=a._emscripten_bind_Mesh___destroy___0=function(){return(Ab=a._emscripten_bind_Mesh___destroy___0=a.asm.T).apply(null,arguments)},Ha=a._emscripten_bind_Metadata_Metadata_0=function(){return(Ha=a._emscripten_bind_Metadata_Metadata_0=a.asm.U).apply(null,arguments)},Bb=a._emscripten_bind_Metadata___destroy___0=function(){return(Bb=a._emscripten_bind_Metadata___destroy___0=
a.asm.V).apply(null,arguments)},Cb=a._emscripten_bind_Status_code_0=function(){return(Cb=a._emscripten_bind_Status_code_0=a.asm.W).apply(null,arguments)},Db=a._emscripten_bind_Status_ok_0=function(){return(Db=a._emscripten_bind_Status_ok_0=a.asm.X).apply(null,arguments)},Eb=a._emscripten_bind_Status_error_msg_0=function(){return(Eb=a._emscripten_bind_Status_error_msg_0=a.asm.Y).apply(null,arguments)},Fb=a._emscripten_bind_Status___destroy___0=function(){return(Fb=a._emscripten_bind_Status___destroy___0=
a.asm.Z).apply(null,arguments)},Ia=a._emscripten_bind_DracoFloat32Array_DracoFloat32Array_0=function(){return(Ia=a._emscripten_bind_DracoFloat32Array_DracoFloat32Array_0=a.asm._).apply(null,arguments)},Gb=a._emscripten_bind_DracoFloat32Array_GetValue_1=function(){return(Gb=a._emscripten_bind_DracoFloat32Array_GetValue_1=a.asm.$).apply(null,arguments)},Hb=a._emscripten_bind_DracoFloat32Array_size_0=function(){return(Hb=a._emscripten_bind_DracoFloat32Array_size_0=a.asm.aa).apply(null,arguments)},Ib=
a._emscripten_bind_DracoFloat32Array___destroy___0=function(){return(Ib=a._emscripten_bind_DracoFloat32Array___destroy___0=a.asm.ba).apply(null,arguments)},Ja=a._emscripten_bind_DracoInt8Array_DracoInt8Array_0=function(){return(Ja=a._emscripten_bind_DracoInt8Array_DracoInt8Array_0=a.asm.ca).apply(null,arguments)},Jb=a._emscripten_bind_DracoInt8Array_GetValue_1=function(){return(Jb=a._emscripten_bind_DracoInt8Array_GetValue_1=a.asm.da).apply(null,arguments)},Kb=a._emscripten_bind_DracoInt8Array_size_0=
function(){return(Kb=a._emscripten_bind_DracoInt8Array_size_0=a.asm.ea).apply(null,arguments)},Lb=a._emscripten_bind_DracoInt8Array___destroy___0=function(){return(Lb=a._emscripten_bind_DracoInt8Array___destroy___0=a.asm.fa).apply(null,arguments)},Ka=a._emscripten_bind_DracoUInt8Array_DracoUInt8Array_0=function(){return(Ka=a._emscripten_bind_DracoUInt8Array_DracoUInt8Array_0=a.asm.ga).apply(null,arguments)},Mb=a._emscripten_bind_DracoUInt8Array_GetValue_1=function(){return(Mb=a._emscripten_bind_DracoUInt8Array_GetValue_1=
a.asm.ha).apply(null,arguments)},Nb=a._emscripten_bind_DracoUInt8Array_size_0=function(){return(Nb=a._emscripten_bind_DracoUInt8Array_size_0=a.asm.ia).apply(null,arguments)},Ob=a._emscripten_bind_DracoUInt8Array___destroy___0=function(){return(Ob=a._emscripten_bind_DracoUInt8Array___destroy___0=a.asm.ja).apply(null,arguments)},La=a._emscripten_bind_DracoInt16Array_DracoInt16Array_0=function(){return(La=a._emscripten_bind_DracoInt16Array_DracoInt16Array_0=a.asm.ka).apply(null,arguments)},Pb=a._emscripten_bind_DracoInt16Array_GetValue_1=
function(){return(Pb=a._emscripten_bind_DracoInt16Array_GetValue_1=a.asm.la).apply(null,arguments)},Qb=a._emscripten_bind_DracoInt16Array_size_0=function(){return(Qb=a._emscripten_bind_DracoInt16Array_size_0=a.asm.ma).apply(null,arguments)},Rb=a._emscripten_bind_DracoInt16Array___destroy___0=function(){return(Rb=a._emscripten_bind_DracoInt16Array___destroy___0=a.asm.na).apply(null,arguments)},Ma=a._emscripten_bind_DracoUInt16Array_DracoUInt16Array_0=function(){return(Ma=a._emscripten_bind_DracoUInt16Array_DracoUInt16Array_0=
a.asm.oa).apply(null,arguments)},Sb=a._emscripten_bind_DracoUInt16Array_GetValue_1=function(){return(Sb=a._emscripten_bind_DracoUInt16Array_GetValue_1=a.asm.pa).apply(null,arguments)},Tb=a._emscripten_bind_DracoUInt16Array_size_0=function(){return(Tb=a._emscripten_bind_DracoUInt16Array_size_0=a.asm.qa).apply(null,arguments)},Ub=a._emscripten_bind_DracoUInt16Array___destroy___0=function(){return(Ub=a._emscripten_bind_DracoUInt16Array___destroy___0=a.asm.ra).apply(null,arguments)},Na=a._emscripten_bind_DracoInt32Array_DracoInt32Array_0=
function(){return(Na=a._emscripten_bind_DracoInt32Array_DracoInt32Array_0=a.asm.sa).apply(null,arguments)},Vb=a._emscripten_bind_DracoInt32Array_GetValue_1=function(){return(Vb=a._emscripten_bind_DracoInt32Array_GetValue_1=a.asm.ta).apply(null,arguments)},Wb=a._emscripten_bind_DracoInt32Array_size_0=function(){return(Wb=a._emscripten_bind_DracoInt32Array_size_0=a.asm.ua).apply(null,arguments)},Xb=a._emscripten_bind_DracoInt32Array___destroy___0=function(){return(Xb=a._emscripten_bind_DracoInt32Array___destroy___0=
a.asm.va).apply(null,arguments)},Oa=a._emscripten_bind_DracoUInt32Array_DracoUInt32Array_0=function(){return(Oa=a._emscripten_bind_DracoUInt32Array_DracoUInt32Array_0=a.asm.wa).apply(null,arguments)},Yb=a._emscripten_bind_DracoUInt32Array_GetValue_1=function(){return(Yb=a._emscripten_bind_DracoUInt32Array_GetValue_1=a.asm.xa).apply(null,arguments)},Zb=a._emscripten_bind_DracoUInt32Array_size_0=function(){return(Zb=a._emscripten_bind_DracoUInt32Array_size_0=a.asm.ya).apply(null,arguments)},$b=a._emscripten_bind_DracoUInt32Array___destroy___0=
function(){return($b=a._emscripten_bind_DracoUInt32Array___destroy___0=a.asm.za).apply(null,arguments)},Pa=a._emscripten_bind_MetadataQuerier_MetadataQuerier_0=function(){return(Pa=a._emscripten_bind_MetadataQuerier_MetadataQuerier_0=a.asm.Aa).apply(null,arguments)},ac=a._emscripten_bind_MetadataQuerier_HasEntry_2=function(){return(ac=a._emscripten_bind_MetadataQuerier_HasEntry_2=a.asm.Ba).apply(null,arguments)},bc=a._emscripten_bind_MetadataQuerier_GetIntEntry_2=function(){return(bc=a._emscripten_bind_MetadataQuerier_GetIntEntry_2=
a.asm.Ca).apply(null,arguments)},cc=a._emscripten_bind_MetadataQuerier_GetIntEntryArray_3=function(){return(cc=a._emscripten_bind_MetadataQuerier_GetIntEntryArray_3=a.asm.Da).apply(null,arguments)},dc=a._emscripten_bind_MetadataQuerier_GetDoubleEntry_2=function(){return(dc=a._emscripten_bind_MetadataQuerier_GetDoubleEntry_2=a.asm.Ea).apply(null,arguments)},ec=a._emscripten_bind_MetadataQuerier_GetStringEntry_2=function(){return(ec=a._emscripten_bind_MetadataQuerier_GetStringEntry_2=a.asm.Fa).apply(null,
arguments)},fc=a._emscripten_bind_MetadataQuerier_NumEntries_1=function(){return(fc=a._emscripten_bind_MetadataQuerier_NumEntries_1=a.asm.Ga).apply(null,arguments)},gc=a._emscripten_bind_MetadataQuerier_GetEntryName_2=function(){return(gc=a._emscripten_bind_MetadataQuerier_GetEntryName_2=a.asm.Ha).apply(null,arguments)},hc=a._emscripten_bind_MetadataQuerier___destroy___0=function(){return(hc=a._emscripten_bind_MetadataQuerier___destroy___0=a.asm.Ia).apply(null,arguments)},Qa=a._emscripten_bind_Decoder_Decoder_0=
function(){return(Qa=a._emscripten_bind_Decoder_Decoder_0=a.asm.Ja).apply(null,arguments)},ic=a._emscripten_bind_Decoder_DecodeArrayToPointCloud_3=function(){return(ic=a._emscripten_bind_Decoder_DecodeArrayToPointCloud_3=a.asm.Ka).apply(null,arguments)},jc=a._emscripten_bind_Decoder_DecodeArrayToMesh_3=function(){return(jc=a._emscripten_bind_Decoder_DecodeArrayToMesh_3=a.asm.La).apply(null,arguments)},kc=a._emscripten_bind_Decoder_GetAttributeId_2=function(){return(kc=a._emscripten_bind_Decoder_GetAttributeId_2=
a.asm.Ma).apply(null,arguments)},lc=a._emscripten_bind_Decoder_GetAttributeIdByName_2=function(){return(lc=a._emscripten_bind_Decoder_GetAttributeIdByName_2=a.asm.Na).apply(null,arguments)},mc=a._emscripten_bind_Decoder_GetAttributeIdByMetadataEntry_3=function(){return(mc=a._emscripten_bind_Decoder_GetAttributeIdByMetadataEntry_3=a.asm.Oa).apply(null,arguments)},nc=a._emscripten_bind_Decoder_GetAttribute_2=function(){return(nc=a._emscripten_bind_Decoder_GetAttribute_2=a.asm.Pa).apply(null,arguments)},
oc=a._emscripten_bind_Decoder_GetAttributeByUniqueId_2=function(){return(oc=a._emscripten_bind_Decoder_GetAttributeByUniqueId_2=a.asm.Qa).apply(null,arguments)},pc=a._emscripten_bind_Decoder_GetMetadata_1=function(){return(pc=a._emscripten_bind_Decoder_GetMetadata_1=a.asm.Ra).apply(null,arguments)},qc=a._emscripten_bind_Decoder_GetAttributeMetadata_2=function(){return(qc=a._emscripten_bind_Decoder_GetAttributeMetadata_2=a.asm.Sa).apply(null,arguments)},rc=a._emscripten_bind_Decoder_GetFaceFromMesh_3=
function(){return(rc=a._emscripten_bind_Decoder_GetFaceFromMesh_3=a.asm.Ta).apply(null,arguments)},sc=a._emscripten_bind_Decoder_GetTriangleStripsFromMesh_2=function(){return(sc=a._emscripten_bind_Decoder_GetTriangleStripsFromMesh_2=a.asm.Ua).apply(null,arguments)},tc=a._emscripten_bind_Decoder_GetTrianglesUInt16Array_3=function(){return(tc=a._emscripten_bind_Decoder_GetTrianglesUInt16Array_3=a.asm.Va).apply(null,arguments)},uc=a._emscripten_bind_Decoder_GetTrianglesUInt32Array_3=function(){return(uc=
a._emscripten_bind_Decoder_GetTrianglesUInt32Array_3=a.asm.Wa).apply(null,arguments)},vc=a._emscripten_bind_Decoder_GetAttributeFloat_3=function(){return(vc=a._emscripten_bind_Decoder_GetAttributeFloat_3=a.asm.Xa).apply(null,arguments)},wc=a._emscripten_bind_Decoder_GetAttributeFloatForAllPoints_3=function(){return(wc=a._emscripten_bind_Decoder_GetAttributeFloatForAllPoints_3=a.asm.Ya).apply(null,arguments)},xc=a._emscripten_bind_Decoder_GetAttributeIntForAllPoints_3=function(){return(xc=a._emscripten_bind_Decoder_GetAttributeIntForAllPoints_3=
a.asm.Za).apply(null,arguments)},yc=a._emscripten_bind_Decoder_GetAttributeInt8ForAllPoints_3=function(){return(yc=a._emscripten_bind_Decoder_GetAttributeInt8ForAllPoints_3=a.asm._a).apply(null,arguments)},zc=a._emscripten_bind_Decoder_GetAttributeUInt8ForAllPoints_3=function(){return(zc=a._emscripten_bind_Decoder_GetAttributeUInt8ForAllPoints_3=a.asm.$a).apply(null,arguments)},Ac=a._emscripten_bind_Decoder_GetAttributeInt16ForAllPoints_3=function(){return(Ac=a._emscripten_bind_Decoder_GetAttributeInt16ForAllPoints_3=
a.asm.ab).apply(null,arguments)},Bc=a._emscripten_bind_Decoder_GetAttributeUInt16ForAllPoints_3=function(){return(Bc=a._emscripten_bind_Decoder_GetAttributeUInt16ForAllPoints_3=a.asm.bb).apply(null,arguments)},Cc=a._emscripten_bind_Decoder_GetAttributeInt32ForAllPoints_3=function(){return(Cc=a._emscripten_bind_Decoder_GetAttributeInt32ForAllPoints_3=a.asm.cb).apply(null,arguments)},Dc=a._emscripten_bind_Decoder_GetAttributeUInt32ForAllPoints_3=function(){return(Dc=a._emscripten_bind_Decoder_GetAttributeUInt32ForAllPoints_3=
a.asm.db).apply(null,arguments)},Ec=a._emscripten_bind_Decoder_GetAttributeDataArrayForAllPoints_5=function(){return(Ec=a._emscripten_bind_Decoder_GetAttributeDataArrayForAllPoints_5=a.asm.eb).apply(null,arguments)},Fc=a._emscripten_bind_Decoder_SkipAttributeTransform_1=function(){return(Fc=a._emscripten_bind_Decoder_SkipAttributeTransform_1=a.asm.fb).apply(null,arguments)},Gc=a._emscripten_bind_Decoder_GetEncodedGeometryType_Deprecated_1=function(){return(Gc=a._emscripten_bind_Decoder_GetEncodedGeometryType_Deprecated_1=
a.asm.gb).apply(null,arguments)},Hc=a._emscripten_bind_Decoder_DecodeBufferToPointCloud_2=function(){return(Hc=a._emscripten_bind_Decoder_DecodeBufferToPointCloud_2=a.asm.hb).apply(null,arguments)},Ic=a._emscripten_bind_Decoder_DecodeBufferToMesh_2=function(){return(Ic=a._emscripten_bind_Decoder_DecodeBufferToMesh_2=a.asm.ib).apply(null,arguments)},Jc=a._emscripten_bind_Decoder___destroy___0=function(){return(Jc=a._emscripten_bind_Decoder___destroy___0=a.asm.jb).apply(null,arguments)},Kc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_INVALID_TRANSFORM=
function(){return(Kc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_INVALID_TRANSFORM=a.asm.kb).apply(null,arguments)},Lc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_NO_TRANSFORM=function(){return(Lc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_NO_TRANSFORM=a.asm.lb).apply(null,arguments)},Mc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_QUANTIZATION_TRANSFORM=function(){return(Mc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_QUANTIZATION_TRANSFORM=
a.asm.mb).apply(null,arguments)},Nc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_OCTAHEDRON_TRANSFORM=function(){return(Nc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_OCTAHEDRON_TRANSFORM=a.asm.nb).apply(null,arguments)},Oc=a._emscripten_enum_draco_GeometryAttribute_Type_INVALID=function(){return(Oc=a._emscripten_enum_draco_GeometryAttribute_Type_INVALID=a.asm.ob).apply(null,arguments)},Pc=a._emscripten_enum_draco_GeometryAttribute_Type_POSITION=function(){return(Pc=a._emscripten_enum_draco_GeometryAttribute_Type_POSITION=
a.asm.pb).apply(null,arguments)},Qc=a._emscripten_enum_draco_GeometryAttribute_Type_NORMAL=function(){return(Qc=a._emscripten_enum_draco_GeometryAttribute_Type_NORMAL=a.asm.qb).apply(null,arguments)},Rc=a._emscripten_enum_draco_GeometryAttribute_Type_COLOR=function(){return(Rc=a._emscripten_enum_draco_GeometryAttribute_Type_COLOR=a.asm.rb).apply(null,arguments)},Sc=a._emscripten_enum_draco_GeometryAttribute_Type_TEX_COORD=function(){return(Sc=a._emscripten_enum_draco_GeometryAttribute_Type_TEX_COORD=
a.asm.sb).apply(null,arguments)},Tc=a._emscripten_enum_draco_GeometryAttribute_Type_GENERIC=function(){return(Tc=a._emscripten_enum_draco_GeometryAttribute_Type_GENERIC=a.asm.tb).apply(null,arguments)},Uc=a._emscripten_enum_draco_EncodedGeometryType_INVALID_GEOMETRY_TYPE=function(){return(Uc=a._emscripten_enum_draco_EncodedGeometryType_INVALID_GEOMETRY_TYPE=a.asm.ub).apply(null,arguments)},Vc=a._emscripten_enum_draco_EncodedGeometryType_POINT_CLOUD=function(){return(Vc=a._emscripten_enum_draco_EncodedGeometryType_POINT_CLOUD=
a.asm.vb).apply(null,arguments)},Wc=a._emscripten_enum_draco_EncodedGeometryType_TRIANGULAR_MESH=function(){return(Wc=a._emscripten_enum_draco_EncodedGeometryType_TRIANGULAR_MESH=a.asm.wb).apply(null,arguments)},Xc=a._emscripten_enum_draco_DataType_DT_INVALID=function(){return(Xc=a._emscripten_enum_draco_DataType_DT_INVALID=a.asm.xb).apply(null,arguments)},Yc=a._emscripten_enum_draco_DataType_DT_INT8=function(){return(Yc=a._emscripten_enum_draco_DataType_DT_INT8=a.asm.yb).apply(null,arguments)},Zc=
a._emscripten_enum_draco_DataType_DT_UINT8=function(){return(Zc=a._emscripten_enum_draco_DataType_DT_UINT8=a.asm.zb).apply(null,arguments)},$c=a._emscripten_enum_draco_DataType_DT_INT16=function(){return($c=a._emscripten_enum_draco_DataType_DT_INT16=a.asm.Ab).apply(null,arguments)},ad=a._emscripten_enum_draco_DataType_DT_UINT16=function(){return(ad=a._emscripten_enum_draco_DataType_DT_UINT16=a.asm.Bb).apply(null,arguments)},bd=a._emscripten_enum_draco_DataType_DT_INT32=function(){return(bd=a._emscripten_enum_draco_DataType_DT_INT32=
a.asm.Cb).apply(null,arguments)},cd=a._emscripten_enum_draco_DataType_DT_UINT32=function(){return(cd=a._emscripten_enum_draco_DataType_DT_UINT32=a.asm.Db).apply(null,arguments)},dd=a._emscripten_enum_draco_DataType_DT_INT64=function(){return(dd=a._emscripten_enum_draco_DataType_DT_INT64=a.asm.Eb).apply(null,arguments)},ed=a._emscripten_enum_draco_DataType_DT_UINT64=function(){return(ed=a._emscripten_enum_draco_DataType_DT_UINT64=a.asm.Fb).apply(null,arguments)},fd=a._emscripten_enum_draco_DataType_DT_FLOAT32=
function(){return(fd=a._emscripten_enum_draco_DataType_DT_FLOAT32=a.asm.Gb).apply(null,arguments)},gd=a._emscripten_enum_draco_DataType_DT_FLOAT64=function(){return(gd=a._emscripten_enum_draco_DataType_DT_FLOAT64=a.asm.Hb).apply(null,arguments)},hd=a._emscripten_enum_draco_DataType_DT_BOOL=function(){return(hd=a._emscripten_enum_draco_DataType_DT_BOOL=a.asm.Ib).apply(null,arguments)},id=a._emscripten_enum_draco_DataType_DT_TYPES_COUNT=function(){return(id=a._emscripten_enum_draco_DataType_DT_TYPES_COUNT=
a.asm.Jb).apply(null,arguments)},jd=a._emscripten_enum_draco_StatusCode_OK=function(){return(jd=a._emscripten_enum_draco_StatusCode_OK=a.asm.Kb).apply(null,arguments)},kd=a._emscripten_enum_draco_StatusCode_DRACO_ERROR=function(){return(kd=a._emscripten_enum_draco_StatusCode_DRACO_ERROR=a.asm.Lb).apply(null,arguments)},ld=a._emscripten_enum_draco_StatusCode_IO_ERROR=function(){return(ld=a._emscripten_enum_draco_StatusCode_IO_ERROR=a.asm.Mb).apply(null,arguments)},md=a._emscripten_enum_draco_StatusCode_INVALID_PARAMETER=
function(){return(md=a._emscripten_enum_draco_StatusCode_INVALID_PARAMETER=a.asm.Nb).apply(null,arguments)},nd=a._emscripten_enum_draco_StatusCode_UNSUPPORTED_VERSION=function(){return(nd=a._emscripten_enum_draco_StatusCode_UNSUPPORTED_VERSION=a.asm.Ob).apply(null,arguments)},od=a._emscripten_enum_draco_StatusCode_UNKNOWN_VERSION=function(){return(od=a._emscripten_enum_draco_StatusCode_UNKNOWN_VERSION=a.asm.Pb).apply(null,arguments)};a._malloc=function(){return(a._malloc=a.asm.Qb).apply(null,arguments)};
a._free=function(){return(a._free=a.asm.Rb).apply(null,arguments)};var ua=function(){return(ua=a.asm.Sb).apply(null,arguments)};a.___start_em_js=11660;a.___stop_em_js=11758;var la;ha=function b(){la||F();la||(ha=b)};if(a.preInit)for("function"==typeof a.preInit&&(a.preInit=[a.preInit]);0<a.preInit.length;)a.preInit.pop()();F();v.prototype=Object.create(v.prototype);v.prototype.constructor=v;v.prototype.__class__=v;v.__cache__={};a.WrapperObject=v;a.getCache=w;a.wrapPointer=B;a.castObject=function(b,
c){return B(b.ptr,c)};a.NULL=B(0);a.destroy=function(b){if(!b.__destroy__)throw"Error: Cannot destroy object. (Did you create it yourself?)";b.__destroy__();delete w(b.__class__)[b.ptr]};a.compare=function(b,c){return b.ptr===c.ptr};a.getPointer=function(b){return b.ptr};a.getClass=function(b){return b.__class__};var r={buffer:0,size:0,pos:0,temps:[],needed:0,prepare:function(){if(r.needed){for(var b=0;b<r.temps.length;b++)a._free(r.temps[b]);r.temps.length=0;a._free(r.buffer);r.buffer=0;r.size+=
r.needed;r.needed=0}r.buffer||(r.size+=128,r.buffer=a._malloc(r.size),r.buffer||y(void 0));r.pos=0},alloc:function(b,c){r.buffer||y(void 0);b=b.length*c.BYTES_PER_ELEMENT;b=b+7&-8;r.pos+b>=r.size?(0<b||y(void 0),r.needed+=b,c=a._malloc(b),r.temps.push(c)):(c=r.buffer+r.pos,r.pos+=b);return c},copy:function(b,c,d){d>>>=0;switch(c.BYTES_PER_ELEMENT){case 2:d>>>=1;break;case 4:d>>>=2;break;case 8:d>>>=3}for(var g=0;g<b.length;g++)c[d+g]=b[g]}};X.prototype=Object.create(v.prototype);X.prototype.constructor=
X;X.prototype.__class__=X;X.__cache__={};a.VoidPtr=X;X.prototype.__destroy__=X.prototype.__destroy__=function(){Xa(this.ptr)};S.prototype=Object.create(v.prototype);S.prototype.constructor=S;S.prototype.__class__=S;S.__cache__={};a.DecoderBuffer=S;S.prototype.Init=S.prototype.Init=function(b,c){var d=this.ptr;r.prepare();"object"==typeof b&&(b=Z(b));c&&"object"===typeof c&&(c=c.ptr);Ya(d,b,c)};S.prototype.__destroy__=S.prototype.__destroy__=function(){Za(this.ptr)};Q.prototype=Object.create(v.prototype);
Q.prototype.constructor=Q;Q.prototype.__class__=Q;Q.__cache__={};a.AttributeTransformData=Q;Q.prototype.transform_type=Q.prototype.transform_type=function(){return $a(this.ptr)};Q.prototype.__destroy__=Q.prototype.__destroy__=function(){ab(this.ptr)};V.prototype=Object.create(v.prototype);V.prototype.constructor=V;V.prototype.__class__=V;V.__cache__={};a.GeometryAttribute=V;V.prototype.__destroy__=V.prototype.__destroy__=function(){bb(this.ptr)};x.prototype=Object.create(v.prototype);x.prototype.constructor=
x;x.prototype.__class__=x;x.__cache__={};a.PointAttribute=x;x.prototype.size=x.prototype.size=function(){return cb(this.ptr)};x.prototype.GetAttributeTransformData=x.prototype.GetAttributeTransformData=function(){return B(db(this.ptr),Q)};x.prototype.attribute_type=x.prototype.attribute_type=function(){return eb(this.ptr)};x.prototype.data_type=x.prototype.data_type=function(){return fb(this.ptr)};x.prototype.num_components=x.prototype.num_components=function(){return gb(this.ptr)};x.prototype.normalized=
x.prototype.normalized=function(){return!!hb(this.ptr)};x.prototype.byte_stride=x.prototype.byte_stride=function(){return ib(this.ptr)};x.prototype.byte_offset=x.prototype.byte_offset=function(){return jb(this.ptr)};x.prototype.unique_id=x.prototype.unique_id=function(){return kb(this.ptr)};x.prototype.__destroy__=x.prototype.__destroy__=function(){lb(this.ptr)};D.prototype=Object.create(v.prototype);D.prototype.constructor=D;D.prototype.__class__=D;D.__cache__={};a.AttributeQuantizationTransform=
D;D.prototype.InitFromAttribute=D.prototype.InitFromAttribute=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return!!mb(c,b)};D.prototype.quantization_bits=D.prototype.quantization_bits=function(){return nb(this.ptr)};D.prototype.min_value=D.prototype.min_value=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return ob(c,b)};D.prototype.range=D.prototype.range=function(){return pb(this.ptr)};D.prototype.__destroy__=D.prototype.__destroy__=function(){qb(this.ptr)};G.prototype=
Object.create(v.prototype);G.prototype.constructor=G;G.prototype.__class__=G;G.__cache__={};a.AttributeOctahedronTransform=G;G.prototype.InitFromAttribute=G.prototype.InitFromAttribute=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return!!rb(c,b)};G.prototype.quantization_bits=G.prototype.quantization_bits=function(){return sb(this.ptr)};G.prototype.__destroy__=G.prototype.__destroy__=function(){tb(this.ptr)};H.prototype=Object.create(v.prototype);H.prototype.constructor=H;H.prototype.__class__=
H;H.__cache__={};a.PointCloud=H;H.prototype.num_attributes=H.prototype.num_attributes=function(){return ub(this.ptr)};H.prototype.num_points=H.prototype.num_points=function(){return vb(this.ptr)};H.prototype.__destroy__=H.prototype.__destroy__=function(){wb(this.ptr)};E.prototype=Object.create(v.prototype);E.prototype.constructor=E;E.prototype.__class__=E;E.__cache__={};a.Mesh=E;E.prototype.num_faces=E.prototype.num_faces=function(){return xb(this.ptr)};E.prototype.num_attributes=E.prototype.num_attributes=
function(){return yb(this.ptr)};E.prototype.num_points=E.prototype.num_points=function(){return zb(this.ptr)};E.prototype.__destroy__=E.prototype.__destroy__=function(){Ab(this.ptr)};T.prototype=Object.create(v.prototype);T.prototype.constructor=T;T.prototype.__class__=T;T.__cache__={};a.Metadata=T;T.prototype.__destroy__=T.prototype.__destroy__=function(){Bb(this.ptr)};C.prototype=Object.create(v.prototype);C.prototype.constructor=C;C.prototype.__class__=C;C.__cache__={};a.Status=C;C.prototype.code=
C.prototype.code=function(){return Cb(this.ptr)};C.prototype.ok=C.prototype.ok=function(){return!!Db(this.ptr)};C.prototype.error_msg=C.prototype.error_msg=function(){return p(Eb(this.ptr))};C.prototype.__destroy__=C.prototype.__destroy__=function(){Fb(this.ptr)};I.prototype=Object.create(v.prototype);I.prototype.constructor=I;I.prototype.__class__=I;I.__cache__={};a.DracoFloat32Array=I;I.prototype.GetValue=I.prototype.GetValue=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return Gb(c,
b)};I.prototype.size=I.prototype.size=function(){return Hb(this.ptr)};I.prototype.__destroy__=I.prototype.__destroy__=function(){Ib(this.ptr)};J.prototype=Object.create(v.prototype);J.prototype.constructor=J;J.prototype.__class__=J;J.__cache__={};a.DracoInt8Array=J;J.prototype.GetValue=J.prototype.GetValue=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return Jb(c,b)};J.prototype.size=J.prototype.size=function(){return Kb(this.ptr)};J.prototype.__destroy__=J.prototype.__destroy__=function(){Lb(this.ptr)};
K.prototype=Object.create(v.prototype);K.prototype.constructor=K;K.prototype.__class__=K;K.__cache__={};a.DracoUInt8Array=K;K.prototype.GetValue=K.prototype.GetValue=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return Mb(c,b)};K.prototype.size=K.prototype.size=function(){return Nb(this.ptr)};K.prototype.__destroy__=K.prototype.__destroy__=function(){Ob(this.ptr)};L.prototype=Object.create(v.prototype);L.prototype.constructor=L;L.prototype.__class__=L;L.__cache__={};a.DracoInt16Array=
L;L.prototype.GetValue=L.prototype.GetValue=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return Pb(c,b)};L.prototype.size=L.prototype.size=function(){return Qb(this.ptr)};L.prototype.__destroy__=L.prototype.__destroy__=function(){Rb(this.ptr)};M.prototype=Object.create(v.prototype);M.prototype.constructor=M;M.prototype.__class__=M;M.__cache__={};a.DracoUInt16Array=M;M.prototype.GetValue=M.prototype.GetValue=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return Sb(c,b)};
M.prototype.size=M.prototype.size=function(){return Tb(this.ptr)};M.prototype.__destroy__=M.prototype.__destroy__=function(){Ub(this.ptr)};N.prototype=Object.create(v.prototype);N.prototype.constructor=N;N.prototype.__class__=N;N.__cache__={};a.DracoInt32Array=N;N.prototype.GetValue=N.prototype.GetValue=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return Vb(c,b)};N.prototype.size=N.prototype.size=function(){return Wb(this.ptr)};N.prototype.__destroy__=N.prototype.__destroy__=function(){Xb(this.ptr)};
O.prototype=Object.create(v.prototype);O.prototype.constructor=O;O.prototype.__class__=O;O.__cache__={};a.DracoUInt32Array=O;O.prototype.GetValue=O.prototype.GetValue=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return Yb(c,b)};O.prototype.size=O.prototype.size=function(){return Zb(this.ptr)};O.prototype.__destroy__=O.prototype.__destroy__=function(){$b(this.ptr)};z.prototype=Object.create(v.prototype);z.prototype.constructor=z;z.prototype.__class__=z;z.__cache__={};a.MetadataQuerier=
z;z.prototype.HasEntry=z.prototype.HasEntry=function(b,c){var d=this.ptr;r.prepare();b&&"object"===typeof b&&(b=b.ptr);c=c&&"object"===typeof c?c.ptr:R(c);return!!ac(d,b,c)};z.prototype.GetIntEntry=z.prototype.GetIntEntry=function(b,c){var d=this.ptr;r.prepare();b&&"object"===typeof b&&(b=b.ptr);c=c&&"object"===typeof c?c.ptr:R(c);return bc(d,b,c)};z.prototype.GetIntEntryArray=z.prototype.GetIntEntryArray=function(b,c,d){var g=this.ptr;r.prepare();b&&"object"===typeof b&&(b=b.ptr);c=c&&"object"===
typeof c?c.ptr:R(c);d&&"object"===typeof d&&(d=d.ptr);cc(g,b,c,d)};z.prototype.GetDoubleEntry=z.prototype.GetDoubleEntry=function(b,c){var d=this.ptr;r.prepare();b&&"object"===typeof b&&(b=b.ptr);c=c&&"object"===typeof c?c.ptr:R(c);return dc(d,b,c)};z.prototype.GetStringEntry=z.prototype.GetStringEntry=function(b,c){var d=this.ptr;r.prepare();b&&"object"===typeof b&&(b=b.ptr);c=c&&"object"===typeof c?c.ptr:R(c);return p(ec(d,b,c))};z.prototype.NumEntries=z.prototype.NumEntries=function(b){var c=this.ptr;
b&&"object"===typeof b&&(b=b.ptr);return fc(c,b)};z.prototype.GetEntryName=z.prototype.GetEntryName=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);return p(gc(d,b,c))};z.prototype.__destroy__=z.prototype.__destroy__=function(){hc(this.ptr)};m.prototype=Object.create(v.prototype);m.prototype.constructor=m;m.prototype.__class__=m;m.__cache__={};a.Decoder=m;m.prototype.DecodeArrayToPointCloud=m.prototype.DecodeArrayToPointCloud=function(b,c,d){var g=
this.ptr;r.prepare();"object"==typeof b&&(b=Z(b));c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return B(ic(g,b,c,d),C)};m.prototype.DecodeArrayToMesh=m.prototype.DecodeArrayToMesh=function(b,c,d){var g=this.ptr;r.prepare();"object"==typeof b&&(b=Z(b));c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return B(jc(g,b,c,d),C)};m.prototype.GetAttributeId=m.prototype.GetAttributeId=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&
(c=c.ptr);return kc(d,b,c)};m.prototype.GetAttributeIdByName=m.prototype.GetAttributeIdByName=function(b,c){var d=this.ptr;r.prepare();b&&"object"===typeof b&&(b=b.ptr);c=c&&"object"===typeof c?c.ptr:R(c);return lc(d,b,c)};m.prototype.GetAttributeIdByMetadataEntry=m.prototype.GetAttributeIdByMetadataEntry=function(b,c,d){var g=this.ptr;r.prepare();b&&"object"===typeof b&&(b=b.ptr);c=c&&"object"===typeof c?c.ptr:R(c);d=d&&"object"===typeof d?d.ptr:R(d);return mc(g,b,c,d)};m.prototype.GetAttribute=
m.prototype.GetAttribute=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);return B(nc(d,b,c),x)};m.prototype.GetAttributeByUniqueId=m.prototype.GetAttributeByUniqueId=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);return B(oc(d,b,c),x)};m.prototype.GetMetadata=m.prototype.GetMetadata=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return B(pc(c,b),T)};m.prototype.GetAttributeMetadata=m.prototype.GetAttributeMetadata=
function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);return B(qc(d,b,c),T)};m.prototype.GetFaceFromMesh=m.prototype.GetFaceFromMesh=function(b,c,d){var g=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!rc(g,b,c,d)};m.prototype.GetTriangleStripsFromMesh=m.prototype.GetTriangleStripsFromMesh=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);
return sc(d,b,c)};m.prototype.GetTrianglesUInt16Array=m.prototype.GetTrianglesUInt16Array=function(b,c,d){var g=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!tc(g,b,c,d)};m.prototype.GetTrianglesUInt32Array=m.prototype.GetTrianglesUInt32Array=function(b,c,d){var g=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!uc(g,b,c,d)};m.prototype.GetAttributeFloat=m.prototype.GetAttributeFloat=
function(b,c,d){var g=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!vc(g,b,c,d)};m.prototype.GetAttributeFloatForAllPoints=m.prototype.GetAttributeFloatForAllPoints=function(b,c,d){var g=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!wc(g,b,c,d)};m.prototype.GetAttributeIntForAllPoints=m.prototype.GetAttributeIntForAllPoints=function(b,c,d){var g=this.ptr;
b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!xc(g,b,c,d)};m.prototype.GetAttributeInt8ForAllPoints=m.prototype.GetAttributeInt8ForAllPoints=function(b,c,d){var g=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!yc(g,b,c,d)};m.prototype.GetAttributeUInt8ForAllPoints=m.prototype.GetAttributeUInt8ForAllPoints=function(b,c,d){var g=this.ptr;b&&"object"===typeof b&&(b=
b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!zc(g,b,c,d)};m.prototype.GetAttributeInt16ForAllPoints=m.prototype.GetAttributeInt16ForAllPoints=function(b,c,d){var g=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!Ac(g,b,c,d)};m.prototype.GetAttributeUInt16ForAllPoints=m.prototype.GetAttributeUInt16ForAllPoints=function(b,c,d){var g=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&
(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!Bc(g,b,c,d)};m.prototype.GetAttributeInt32ForAllPoints=m.prototype.GetAttributeInt32ForAllPoints=function(b,c,d){var g=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!Cc(g,b,c,d)};m.prototype.GetAttributeUInt32ForAllPoints=m.prototype.GetAttributeUInt32ForAllPoints=function(b,c,d){var g=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===
typeof d&&(d=d.ptr);return!!Dc(g,b,c,d)};m.prototype.GetAttributeDataArrayForAllPoints=m.prototype.GetAttributeDataArrayForAllPoints=function(b,c,d,g,t){var aa=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);g&&"object"===typeof g&&(g=g.ptr);t&&"object"===typeof t&&(t=t.ptr);return!!Ec(aa,b,c,d,g,t)};m.prototype.SkipAttributeTransform=m.prototype.SkipAttributeTransform=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);Fc(c,
b)};m.prototype.GetEncodedGeometryType_Deprecated=m.prototype.GetEncodedGeometryType_Deprecated=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return Gc(c,b)};m.prototype.DecodeBufferToPointCloud=m.prototype.DecodeBufferToPointCloud=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);return B(Hc(d,b,c),C)};m.prototype.DecodeBufferToMesh=m.prototype.DecodeBufferToMesh=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===
typeof c&&(c=c.ptr);return B(Ic(d,b,c),C)};m.prototype.__destroy__=m.prototype.__destroy__=function(){Jc(this.ptr)};(function(){function b(){a.ATTRIBUTE_INVALID_TRANSFORM=Kc();a.ATTRIBUTE_NO_TRANSFORM=Lc();a.ATTRIBUTE_QUANTIZATION_TRANSFORM=Mc();a.ATTRIBUTE_OCTAHEDRON_TRANSFORM=Nc();a.INVALID=Oc();a.POSITION=Pc();a.NORMAL=Qc();a.COLOR=Rc();a.TEX_COORD=Sc();a.GENERIC=Tc();a.INVALID_GEOMETRY_TYPE=Uc();a.POINT_CLOUD=Vc();a.TRIANGULAR_MESH=Wc();a.DT_INVALID=Xc();a.DT_INT8=Yc();a.DT_UINT8=Zc();a.DT_INT16=
$c();a.DT_UINT16=ad();a.DT_INT32=bd();a.DT_UINT32=cd();a.DT_INT64=dd();a.DT_UINT64=ed();a.DT_FLOAT32=fd();a.DT_FLOAT64=gd();a.DT_BOOL=hd();a.DT_TYPES_COUNT=id();a.OK=jd();a.DRACO_ERROR=kd();a.IO_ERROR=ld();a.INVALID_PARAMETER=md();a.UNSUPPORTED_VERSION=nd();a.UNKNOWN_VERSION=od()}va?b():oa.unshift(b)})();if("function"===typeof a.onModuleParsed)a.onModuleParsed();a.Decoder.prototype.GetEncodedGeometryType=function(b){if(b.__class__&&b.__class__===a.DecoderBuffer)return a.Decoder.prototype.GetEncodedGeometryType_Deprecated(b);
if(8>b.byteLength)return a.INVALID_GEOMETRY_TYPE;switch(b[7]){case 0:return a.POINT_CLOUD;case 1:return a.TRIANGULAR_MESH;default:return a.INVALID_GEOMETRY_TYPE}};return n.ready}}();"object"===typeof exports&&"object"===typeof module?module.exports=DracoDecoderModule:"function"===typeof define&&define.amd?define([],function(){return DracoDecoderModule}):"object"===typeof exports&&(exports.DracoDecoderModule=DracoDecoderModule);

File diff suppressed because it is too large Load Diff

View File

@@ -2,7 +2,7 @@
{
"id": "cameraParallax",
"contentUrl": "https://resources.gdevelop-app.com/in-app-tutorials/cameraParallax.json",
"availableLocales": ["en", "fr", "es", "pt", "th"],
"availableLocales": ["en", "fr", "es", "pt", "th", "ar"],
"initialTemplateUrl": "https://resources.gdevelop-app.com/in-app-tutorials/templates/cameraParallax/game.json",
"initialProjectData": {
"cameraScene": "CameraScene",
@@ -14,29 +14,36 @@
{
"id": "flingGame",
"contentUrl": "https://resources.gdevelop-app.com/in-app-tutorials/flingGame.json",
"availableLocales": ["en", "fr-FR", "th-TH"]
"availableLocales": ["en", "fr", "es", "pt", "th"]
},
{
"id": "healthBar",
"contentUrl": "https://resources.gdevelop-app.com/in-app-tutorials/healthBar.json",
"availableLocales": ["en", "fr", "es", "pt", "th"],
"availableLocales": ["en", "fr", "es", "pt", "th", "ar"],
"initialTemplateUrl": "https://resources.gdevelop-app.com/in-app-tutorials/templates/healthBar/game.json",
"initialProjectData": { "level": "Level", "player": "Player" }
},
{
"id": "joystick",
"contentUrl": "https://resources.gdevelop-app.com/in-app-tutorials/joystick.json",
"availableLocales": ["en", "fr", "es", "pt", "th"],
"availableLocales": ["en", "fr", "es", "pt", "th", "ar"],
"initialTemplateUrl": "https://resources.gdevelop-app.com/in-app-tutorials/templates/joystick/game.json",
"initialProjectData": {
"gameScene": "GameScene",
"ship": "OrangePlayerShip3"
}
},
{
"id": "object3d",
"contentUrl": "https://resources.gdevelop-app.com/in-app-tutorials/object3d.json",
"availableLocales": ["en", "fr", "es", "pt", "th", "ar"],
"initialTemplateUrl": "https://resources.gdevelop-app.com/in-app-tutorials/templates/object3d/game.json",
"initialProjectData": { "gameScene": "GameScene" }
},
{
"id": "plinkoMultiplier",
"contentUrl": "https://resources.gdevelop-app.com/in-app-tutorials/plinkoMultiplier.json",
"availableLocales": ["en", "fr", "es", "pt", "th"],
"availableLocales": ["en", "fr", "es", "pt", "th", "ar"],
"initialTemplateUrl": "https://resources.gdevelop-app.com/in-app-tutorials/templates/plinkoMultiplier/game.json",
"initialProjectData": {
"gameScene": "GameScene",
@@ -49,10 +56,8 @@
{
"id": "timer",
"contentUrl": "https://resources.gdevelop-app.com/in-app-tutorials/timer.json",
"availableLocales": ["en", "fr", "es", "pt", "th"],
"availableLocales": ["en", "fr", "es", "pt", "th", "ar"],
"initialTemplateUrl": "https://resources.gdevelop-app.com/in-app-tutorials/templates/timer/game.json",
"initialProjectData": {
"gameScene": "GameScene"
}
"initialProjectData": { "gameScene": "GameScene" }
}
]

View File

@@ -1,5 +1,5 @@
const convertCommonMarkdownToPythonMarkdown = content => {
return content.replace(/((\n[-*].*)+)/gm, '\n$1');
return content.replace(/((\n[ \t]{0,2}[-*].*)+)/gm, '\n$1');
};
module.exports = { convertCommonMarkdownToPythonMarkdown };

View File

@@ -133,6 +133,7 @@ export const AssetStoreContext = React.createContext<AssetStoreState>({
});
type AssetStoreStateProviderProps = {|
onlyAppStorePrivateAssetPacks?: ?boolean,
children: React.Node,
|};
@@ -153,6 +154,7 @@ const getAssetPackRandomOrdering = (length: number): Array<number> => {
};
export const AssetStoreStateProvider = ({
onlyAppStorePrivateAssetPacks,
children,
}: AssetStoreStateProviderProps) => {
const [assetShortHeadersById, setAssetShortHeadersById] = React.useState<?{
@@ -262,7 +264,9 @@ export const AssetStoreStateProvider = ({
} = await listAllPublicAssets({ environment });
const authors = await listAllAuthors({ environment });
const licenses = await listAllLicenses({ environment });
const privateAssetPacks = await listListedPrivateAssetPacks();
const privateAssetPacks = await listListedPrivateAssetPacks({
onlyAppStorePrivateAssetPacks,
});
console.info(
`Loaded ${
@@ -284,7 +288,7 @@ export const AssetStoreStateProvider = ({
}
})();
},
[environment]
[environment, onlyAppStorePrivateAssetPacks]
);
// When the public assets or the private assets are loaded, regenerate the

View File

@@ -6,7 +6,7 @@ import GridListTile from '@material-ui/core/GridListTile';
import GridList from '@material-ui/core/GridList';
import { CorsAwareImage } from '../UI/CorsAwareImage';
import Text from '../UI/Text';
import PriceTag from '../UI/PriceTag';
import { PrivateAssetPackPriceTag } from './PrivateAssets/PrivateAssetPackPriceTag';
import type {
PublicAssetPacks,
PublicAssetPack,
@@ -204,8 +204,8 @@ export const PrivateAssetPackTile = ({
alt={`Preview image of asset pack ${assetPackListingData.name}`}
/>
<div style={styles.priceTagContainer}>
<PriceTag
value={assetPackListingData.prices[0].value}
<PrivateAssetPackPriceTag
privateAssetPackListingData={assetPackListingData}
withOverlay
owned={owned}
/>

View File

@@ -10,7 +10,6 @@ import Text from '../../UI/Text';
import { t, Trans } from '@lingui/macro';
import Grid from '@material-ui/core/Grid';
import GridList from '@material-ui/core/GridList';
import { formatPrice } from '../../UI/PriceTag';
import AlertMessage from '../../UI/AlertMessage';
import PlaceholderLoader from '../../UI/PlaceholderLoader';
import { ResponsiveLineStackLayout, LineStackLayout } from '../../UI/Layout';
@@ -32,6 +31,11 @@ import Paper from '../../UI/Paper';
import Window from '../../Utils/Window';
import ScrollView from '../../UI/ScrollView';
import { PrivateAssetPackTile } from '../AssetsHome';
import {
purchaseAppStoreProduct,
shouldUseAppStoreProduct,
} from '../../Utils/AppStorePurchases';
import { formatPrivateAssetPackPrice } from './PrivateAssetPackPriceTag';
const sameCreatorPackCountForSmallWindow = 2;
const sameCreatorPackCountForMediumWindow = 3;
@@ -78,7 +82,7 @@ const PrivateAssetPackInformationPage = ({
isPurchaseDialogOpen,
onAssetPackOpen,
}: Props) => {
const { id, name, sellerId, prices } = privateAssetPackListingData;
const { id, name, sellerId } = privateAssetPackListingData;
const [assetPack, setAssetPack] = React.useState<?PrivateAssetPack>(null);
const [isFetching, setIsFetching] = React.useState<boolean>(false);
const [
@@ -89,6 +93,10 @@ const PrivateAssetPackInformationPage = ({
sellerPublicProfile,
setSellerPublicProfile,
] = React.useState<?UserPublicProfile>(null);
const [
appStoreProductBeingBought,
setAppStoreProductBeingBought,
] = React.useState(false);
const [errorText, setErrorText] = React.useState<?React.Node>(null);
const windowWidth = useResponsiveWindowWidth();
@@ -97,8 +105,11 @@ const PrivateAssetPackInformationPage = ({
(async () => {
setIsFetching(true);
try {
const assetPack = await getPrivateAssetPack(id);
const profile = await getUserPublicProfile(sellerId);
const [assetPack, profile] = await Promise.all([
getPrivateAssetPack(id),
getUserPublicProfile(sellerId),
]);
setAssetPack(assetPack);
setSellerPublicProfile(profile);
} catch (error) {
@@ -119,43 +130,64 @@ const PrivateAssetPackInformationPage = ({
}
})();
},
[id, sellerId]
[id, sellerId, privateAssetPackListingData.appStoreProductId]
);
const onClickBuy = () => {
const onClickBuy = async () => {
if (!assetPack) return;
try {
onOpenPurchaseDialog();
sendAssetPackBuyClicked({
assetPackId: assetPack.id,
assetPackName: assetPack.name,
assetPackTag: assetPack.tag,
assetPackKind: 'private',
});
if (shouldUseAppStoreProduct()) {
try {
setAppStoreProductBeingBought(true);
await purchaseAppStoreProduct(
privateAssetPackListingData.appStoreProductId
);
} finally {
setAppStoreProductBeingBought(false);
}
} else {
onOpenPurchaseDialog();
}
} catch (e) {
console.warn('Unable to send event', e);
}
};
const getBuyButton = i18n =>
!errorText ? (
const getBuyButton = i18n => {
if (errorText) return null;
const label = !assetPack ? (
<Trans>Loading...</Trans>
) : isPurchaseDialogOpen || appStoreProductBeingBought ? (
<Trans>Processing...</Trans>
) : (
<Trans>
Buy for{' '}
{formatPrivateAssetPackPrice({ i18n, privateAssetPackListingData })}
</Trans>
);
const disabled =
!assetPack || isPurchaseDialogOpen || appStoreProductBeingBought;
return (
<RaisedButton
key="buy-asset-pack"
primary
label={
!assetPack ? (
<Trans>Loading...</Trans>
) : isPurchaseDialogOpen ? (
<Trans>Processing...</Trans>
) : (
<Trans>Buy for {formatPrice(i18n, prices[0].value)}</Trans>
)
}
label={label}
onClick={onClickBuy}
disabled={!assetPack || isPurchaseDialogOpen}
disabled={disabled}
id="buy-asset-pack"
/>
) : null;
);
};
const mediaItems = assetPack
? assetPack.previewImageUrls
@@ -221,7 +253,10 @@ const PrivateAssetPackInformationPage = ({
alignItems="center"
>
<Text noMargin size="block-title">
{formatPrice(i18n, prices[0].value)}
{formatPrivateAssetPackPrice({
i18n,
privateAssetPackListingData,
})}
</Text>
{getBuyButton(i18n)}
</Line>

View File

@@ -0,0 +1,66 @@
// @flow
import * as React from 'react';
import { Trans } from '@lingui/macro';
import { I18n } from '@lingui/react';
import { type I18n as I18nType } from '@lingui/core';
import PriceTag from '../../UI/PriceTag';
import { type PrivateAssetPackListingData } from '../../Utils/GDevelopServices/Shop';
import {
shouldUseAppStoreProduct,
getAppStoreProduct,
} from '../../Utils/AppStorePurchases';
type FormatProps = {|
privateAssetPackListingData: PrivateAssetPackListingData,
i18n: I18nType,
|};
export const formatPrivateAssetPackPrice = ({
i18n,
privateAssetPackListingData,
}: FormatProps): string => {
const appStoreProduct = shouldUseAppStoreProduct()
? getAppStoreProduct(privateAssetPackListingData.appStoreProductId)
: null;
if (appStoreProduct) return appStoreProduct.price;
const stripePrice = privateAssetPackListingData.prices[0];
if (!stripePrice) return '';
return `${i18n
.number(stripePrice.value / 100, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})
.replace(/\D00$/, '')}`;
};
type PrivateAssetPackPriceTagProps = {|
privateAssetPackListingData: PrivateAssetPackListingData,
/**
* To be used when the component is over an element for which
* we don't control the background (e.g. an image).
*/
withOverlay?: boolean,
owned?: boolean,
|};
export const PrivateAssetPackPriceTag = ({
privateAssetPackListingData,
withOverlay,
owned,
}: PrivateAssetPackPriceTagProps) => {
return (
<I18n>
{({ i18n }) => {
const label = owned ? (
<Trans> Owned</Trans>
) : (
formatPrivateAssetPackPrice({ i18n, privateAssetPackListingData })
);
return <PriceTag withOverlay={withOverlay} label={label} />;
}}
</I18n>
);
};

View File

@@ -486,6 +486,7 @@ const BehaviorsEditor = (props: Props) => {
helpPagePath={behaviorMetadata.getHelpPath()}
/>,
<ElementWithMenu
key="menu"
element={
<IconButton size="small">
<ThreeDotsMenu />

View File

@@ -51,7 +51,7 @@ const styles = {
},
};
type Item = NamedCommand | CommandOption | AlgoliaSearchHitType;
type Item = NamedCommand | CommandOption | GoToWikiCommand;
const HitPrimaryText = (
hit: any,

View File

@@ -543,9 +543,9 @@ export default function EffectsList(props: Props) {
>
{connectDragSource(
<span>
<Spacer />
<DragHandleIcon />
<Spacer />
<Column>
<DragHandleIcon />
</Column>
</span>
)}
<ResponsiveLineStackLayout expand>

View File

@@ -415,9 +415,9 @@ export default function EventsBasedBehaviorPropertiesEditor(props: Props) {
>
{connectDragSource(
<span>
<Spacer />
<DragHandleIcon />
<Spacer />
<Column>
<DragHandleIcon />
</Column>
</span>
)}
<ResponsiveLineStackLayout expand>

View File

@@ -76,6 +76,19 @@ export default React.forwardRef<ParameterFieldProps, ParameterFieldInterface>(
})
.filter(Boolean)
.sort();
} else if (object.getType() === 'Scene3D::Model3DObject') {
const model3DConfiguration = gd.asModel3DConfiguration(
object.getConfiguration()
);
return mapFor(0, model3DConfiguration.getAnimationsCount(), index => {
const animationName = model3DConfiguration
.getAnimation(index)
.getName();
return animationName.length > 0 ? animationName : null;
})
.filter(Boolean)
.sort();
}
return [];

View File

@@ -19,7 +19,7 @@ const operatorLabels = {
'/': t`/ (divide by)`,
};
const mapTypeToOperators = {
const mapTypeToOperators: { [string]: Array<string> } = {
unknown: Object.keys(operatorLabels),
number: ['=', '+', '-', '*', '/'],
string: ['=', '+'],
@@ -47,6 +47,10 @@ export default React.forwardRef<ParameterFieldProps, ParameterFieldInterface>(
const operators =
mapTypeToOperators[comparedValueType] || mapTypeToOperators.unknown;
if (!props.value && comparedValueType !== 'unknown') {
props.onChange('=');
}
return (
<SelectField
margin={props.isInline ? 'none' : 'dense'}

View File

@@ -28,6 +28,13 @@ const mapTypeToOperators: { [string]: Array<string> } = {
color: ['=', '!='],
};
const defaultOperators: { [string]: string } = {
number: '=',
time: '>=',
string: '=',
color: '=',
};
export default React.forwardRef<ParameterFieldProps, ParameterFieldInterface>(
function RelationalOperatorField(props: ParameterFieldProps, ref) {
const field = React.useRef<?SelectFieldInterface>(null);
@@ -49,6 +56,13 @@ export default React.forwardRef<ParameterFieldProps, ParameterFieldInterface>(
const operators =
mapTypeToOperators[comparedValueType] || mapTypeToOperators.unknown;
if (!props.value) {
const defaultOperator = defaultOperators[comparedValueType];
if (defaultOperator) {
props.onChange(defaultOperator);
}
}
return (
<SelectField
margin={props.isInline ? 'none' : 'dense'}

View File

@@ -1,37 +0,0 @@
// @flow
import * as PIXI from 'pixi.js-legacy';
import * as THREE from 'three';
import { rgbToHexNumber } from '../Utils/ColorTransformer';
type Props = {|
layout: gdLayout,
|};
export default class BackgroundColor {
layout: gdLayout;
constructor({ layout }: Props) {
this.layout = layout;
}
setBackgroundColorForPixi(pixiRenderer: PIXI.Renderer) {
pixiRenderer.backgroundColor = rgbToHexNumber(
this.layout.getBackgroundColorRed(),
this.layout.getBackgroundColorGreen(),
this.layout.getBackgroundColorBlue()
);
}
setBackgroundColorForThree(
threeRenderer: THREE.Renderer,
threeScene: THREE.Scene
) {
const colorCode = rgbToHexNumber(
this.layout.getBackgroundColorRed(),
this.layout.getBackgroundColorGreen(),
this.layout.getBackgroundColorBlue()
);
threeRenderer.setClearColor(colorCode);
threeScene.background = new THREE.Color(colorCode);
}
}

View File

@@ -7,7 +7,9 @@ import Rectangle from '../Utils/Rectangle';
export default class HighlightedInstance {
instanceMeasurer: InstanceMeasurer;
toCanvasCoordinates: (x: number, y: number) => [number, number];
isInstanceOf3DObject: gdInitialInstance => boolean;
highlightedInstance: gdInitialInstance | null;
isHighlightedInstanceOf3DObject: boolean;
highlightRectangle: PIXI.Container;
tooltipBackground: PIXI.Container;
tooltipText: PIXI.Container;
@@ -15,14 +17,18 @@ export default class HighlightedInstance {
constructor({
instanceMeasurer,
toCanvasCoordinates,
isInstanceOf3DObject,
}: {
instanceMeasurer: InstanceMeasurer,
toCanvasCoordinates: (x: number, y: number) => [number, number],
isInstanceOf3DObject: gdInitialInstance => boolean,
}) {
this.instanceMeasurer = instanceMeasurer;
this.toCanvasCoordinates = toCanvasCoordinates;
this.isInstanceOf3DObject = isInstanceOf3DObject;
this.highlightedInstance = null;
this.isHighlightedInstanceOf3DObject = false;
this.highlightRectangle = new PIXI.Graphics();
this.highlightRectangle.hitArea = new PIXI.Rectangle(0, 0, 0, 0);
@@ -37,6 +43,9 @@ export default class HighlightedInstance {
}
setInstance(instance: gdInitialInstance | null) {
this.isHighlightedInstanceOf3DObject = instance
? this.isInstanceOf3DObject(instance)
: false;
this.highlightedInstance = instance;
}
@@ -84,13 +93,19 @@ export default class HighlightedInstance {
Math.round(highlightedInstance.getX() * 100) / 100 + // An instance position can have a lot of decimals, so round to 2 decimals.
' Y: ' +
Math.round(highlightedInstance.getY() * 100) / 100 + // An instance position can have a lot of decimals, so round to 2 decimals.
(this.isHighlightedInstanceOf3DObject
? ' Z: ' +
// An instance position can have a lot of decimals, so round to 2 decimals.
Math.round(highlightedInstance.getZ() * 100) / 100
: '') +
'\n' +
'Layer: ' +
(highlightedInstance.getLayer() || 'Base layer') +
'\n' +
'Z: ' +
highlightedInstance.getZOrder() +
(this.isHighlightedInstanceOf3DObject
? ''
: '\nZ order: ' + highlightedInstance.getZOrder()) +
'\n';
this.tooltipText.text = tooltipInfo;
this.tooltipText.x = Math.round(

View File

@@ -8,9 +8,7 @@ import Background from '../../UI/Background';
import enumerateLayers from '../../LayersList/EnumerateLayers';
import EmptyMessage from '../../UI/EmptyMessage';
import PropertiesEditor from '../../PropertiesEditor';
import propertiesMapToSchema, {
reorganizeSchemaFor3DInstance,
} from '../../PropertiesEditor/PropertiesMapToSchema';
import propertiesMapToSchema from '../../PropertiesEditor/PropertiesMapToSchema';
import { type Schema } from '../../PropertiesEditor';
import getObjectByName from '../../Utils/GetObjectByName';
import IconButton from '../../UI/IconButton';
@@ -31,7 +29,7 @@ type Props = {|
instances: Array<gdInitialInstance>,
onEditObjectByName: string => void,
onInstancesModified?: (Array<gdInitialInstance>) => void,
onGetInstanceSize: gdInitialInstance => [number, number],
onGetInstanceSize: gdInitialInstance => [number, number, number],
editInstanceVariables: gdInitialInstance => void,
unsavedChanges?: ?UnsavedChanges,
i18n: I18nType,
@@ -40,6 +38,241 @@ type Props = {|
export type InstancePropertiesEditorInterface = {| forceUpdate: () => void |};
const makeSchema = ({
is3DInstance,
i18n,
forceUpdate,
onEditObjectByName,
onGetInstanceSize,
layout,
}) => {
const getInstanceWidth = (instance: gdInitialInstance) =>
instance.hasCustomSize()
? instance.getCustomWidth()
: onGetInstanceSize(instance)[0];
const getInstanceHeight = (instance: gdInitialInstance) =>
instance.hasCustomSize()
? instance.getCustomHeight()
: onGetInstanceSize(instance)[1];
const getInstanceDepth = (instance: gdInitialInstance) =>
instance.hasCustomDepth()
? instance.getCustomDepth()
: onGetInstanceSize(instance)[2];
return [
{
name: i18n._(t`Object`),
getValue: (instance: gdInitialInstance) => instance.getObjectName(),
nonFieldType: 'sectionTitle',
defaultValue: i18n._(t`Different objects`),
},
{
label: i18n._(t`Edit object`),
disabled: 'onValuesDifferent',
nonFieldType: 'button',
getValue: (instance: gdInitialInstance) => instance.getObjectName(),
onClick: (instance: gdInitialInstance) =>
onEditObjectByName(instance.getObjectName()),
},
{
name: i18n._(t`Instance`),
nonFieldType: 'sectionTitle',
},
{
name: 'Position',
type: 'row',
children: [
{
name: 'X',
getLabel: () => i18n._(t`X`),
valueType: 'number',
getValue: (instance: gdInitialInstance) => instance.getX(),
setValue: (instance: gdInitialInstance, newValue: number) =>
instance.setX(newValue),
},
{
name: 'Y',
getLabel: () => i18n._(t`Y`),
valueType: 'number',
getValue: (instance: gdInitialInstance) => instance.getY(),
setValue: (instance: gdInitialInstance, newValue: number) =>
instance.setY(newValue),
},
is3DInstance
? {
name: 'Z',
getLabel: () => i18n._(t`Z`),
valueType: 'number',
getValue: (instance: gdInitialInstance) => instance.getZ(),
setValue: (instance: gdInitialInstance, newValue: number) =>
instance.setZ(newValue),
}
: null,
].filter(Boolean),
},
{
name: 'Angles',
type: 'row',
children: [
is3DInstance
? {
name: 'RotationX',
getLabel: () => i18n._(t`Rotation (X)`),
valueType: 'number',
getValue: (instance: gdInitialInstance) =>
instance.getRotationX(),
setValue: (instance: gdInitialInstance, newValue: number) =>
instance.setRotationX(newValue),
}
: null,
is3DInstance
? {
name: 'RotationY',
getLabel: () => i18n._(t`Rotation (Y)`),
valueType: 'number',
getValue: (instance: gdInitialInstance) =>
instance.getRotationY(),
setValue: (instance: gdInitialInstance, newValue: number) =>
instance.setRotationY(newValue),
}
: null,
{
name: 'Angle',
getLabel: () =>
is3DInstance ? i18n._(t`Rotation (Z)`) : i18n._(t`Angle`),
valueType: 'number',
getValue: (instance: gdInitialInstance) => instance.getAngle(),
setValue: (instance: gdInitialInstance, newValue: number) =>
instance.setAngle(newValue),
},
].filter(Boolean),
},
{
name: 'Lock instance position angle',
getLabel: () => i18n._(t`Lock position/angle in the editor`),
valueType: 'boolean',
getValue: (instance: gdInitialInstance) => instance.isLocked(),
setValue: (instance: gdInitialInstance, newValue: boolean) => {
instance.setLocked(newValue);
if (!newValue) {
instance.setSealed(newValue);
}
},
},
{
name: 'Prevent instance selection',
getLabel: () => i18n._(t`Prevent selection in the editor`),
valueType: 'boolean',
disabled: (instances: gdInitialInstance[]) => {
return instances.some(instance => !instance.isLocked());
},
getValue: (instance: gdInitialInstance) => instance.isSealed(),
setValue: (instance: gdInitialInstance, newValue: boolean) =>
instance.setSealed(newValue),
},
!is3DInstance
? {
name: 'Z Order',
getLabel: () => i18n._(t`Z Order`),
valueType: 'number',
getValue: (instance: gdInitialInstance) => instance.getZOrder(),
setValue: (instance: gdInitialInstance, newValue: number) =>
instance.setZOrder(newValue),
}
: null,
{
name: 'Layer',
getLabel: () => i18n._(t`Layer`),
valueType: 'string',
getChoices: () => enumerateLayers(layout),
getValue: (instance: gdInitialInstance) => instance.getLayer(),
setValue: (instance: gdInitialInstance, newValue: string) =>
instance.setLayer(newValue),
},
{
name: 'Custom size',
getLabel: () => i18n._(t`Custom size`),
valueType: 'boolean',
getValue: (instance: gdInitialInstance) => instance.hasCustomSize(),
setValue: (instance: gdInitialInstance, newValue: boolean) => {
if (
instance.getCustomHeight() === 0 &&
instance.getCustomWidth() === 0 &&
instance.getCustomDepth() === 0
) {
// The instance custom dimensions have never been set before.
// To avoid setting setting all the dimensions to 0 when enabling
// the instance custom size flag, the current instance dimensions are used.
instance.setCustomWidth(getInstanceWidth(instance));
instance.setCustomHeight(getInstanceHeight(instance));
instance.setCustomDepth(getInstanceDepth(instance));
}
instance.setHasCustomSize(newValue);
instance.setHasCustomDepth(newValue);
forceUpdate();
},
},
{
name: 'custom-size-row',
type: 'row',
children: [
{
name: 'Width',
getLabel: () => i18n._(t`Width`),
valueType: 'number',
getValue: getInstanceWidth,
setValue: (instance: gdInitialInstance, newValue: number) => {
instance.setCustomWidth(Math.max(newValue, 0));
instance.setCustomHeight(getInstanceHeight(instance));
instance.setCustomDepth(getInstanceDepth(instance));
// This must be done after reading the size.
instance.setHasCustomSize(true);
instance.setHasCustomDepth(true);
forceUpdate();
},
},
{
name: 'Height',
getLabel: () => i18n._(t`Height`),
valueType: 'number',
getValue: getInstanceHeight,
setValue: (instance: gdInitialInstance, newValue: number) => {
instance.setCustomWidth(getInstanceWidth(instance));
instance.setCustomHeight(Math.max(newValue, 0));
instance.setCustomDepth(getInstanceDepth(instance));
// This must be done after reading the size.
instance.setHasCustomSize(true);
instance.setHasCustomDepth(true);
forceUpdate();
},
},
is3DInstance
? {
name: 'Depth',
getLabel: () => i18n._(t`Depth`),
valueType: 'number',
getValue: getInstanceDepth,
setValue: (instance: gdInitialInstance, newValue: number) => {
instance.setCustomWidth(getInstanceWidth(instance));
instance.setCustomHeight(getInstanceHeight(instance));
instance.setCustomDepth(Math.max(newValue, 0));
// This must be done after reading the size.
instance.setHasCustomSize(true);
instance.setHasCustomDepth(true);
forceUpdate();
},
}
: null,
].filter(Boolean),
},
].filter(Boolean);
};
const InstancePropertiesEditor = ({
instances,
i18n,
@@ -54,182 +287,46 @@ const InstancePropertiesEditor = ({
}: Props) => {
const forceUpdate = useForceUpdate();
const getInstanceWidth = React.useCallback(
(instance: gdInitialInstance) =>
instance.hasCustomSize()
? instance.getCustomWidth() || onGetInstanceSize(instance)[0]
: onGetInstanceSize(instance)[0],
[onGetInstanceSize]
const schemaFor2D: Schema = React.useMemo(
() =>
makeSchema({
i18n,
is3DInstance: false,
onGetInstanceSize,
onEditObjectByName,
layout,
forceUpdate,
}),
[i18n, onGetInstanceSize, onEditObjectByName, layout, forceUpdate]
);
const getInstanceHeight = React.useCallback(
(instance: gdInitialInstance) =>
instance.hasCustomSize()
? instance.getCustomHeight() || onGetInstanceSize(instance)[1]
: onGetInstanceSize(instance)[1],
[onGetInstanceSize]
);
const schema: Schema = React.useMemo(
() => [
{
name: i18n._(t`Object`),
getValue: (instance: gdInitialInstance) => instance.getObjectName(),
nonFieldType: 'sectionTitle',
defaultValue: i18n._(t`Different objects`),
},
{
label: i18n._(t`Edit object`),
disabled: 'onValuesDifferent',
nonFieldType: 'button',
getValue: (instance: gdInitialInstance) => instance.getObjectName(),
onClick: (instance: gdInitialInstance) =>
onEditObjectByName(instance.getObjectName()),
},
{
name: i18n._(t`Instance`),
nonFieldType: 'sectionTitle',
},
{
name: 'Position',
type: 'row',
children: [
{
name: 'X',
getLabel: () => i18n._(t`X`),
valueType: 'number',
getValue: (instance: gdInitialInstance) => instance.getX(),
setValue: (instance: gdInitialInstance, newValue: number) =>
instance.setX(newValue),
},
{
name: 'Y',
getLabel: () => i18n._(t`Y`),
valueType: 'number',
getValue: (instance: gdInitialInstance) => instance.getY(),
setValue: (instance: gdInitialInstance, newValue: number) =>
instance.setY(newValue),
},
],
},
{
name: 'Angles',
type: 'row',
children: [
{
name: 'Angle',
getLabel: () => i18n._(t`Angle`),
valueType: 'number',
getValue: (instance: gdInitialInstance) => instance.getAngle(),
setValue: (instance: gdInitialInstance, newValue: number) =>
instance.setAngle(newValue),
},
],
},
{
name: 'Lock instance position angle',
getLabel: () => i18n._(t`Lock position/angle in the editor`),
valueType: 'boolean',
getValue: (instance: gdInitialInstance) => instance.isLocked(),
setValue: (instance: gdInitialInstance, newValue: boolean) => {
instance.setLocked(newValue);
if (!newValue) {
instance.setSealed(newValue);
}
},
},
{
name: 'Prevent instance selection',
getLabel: () => i18n._(t`Prevent selection in the editor`),
valueType: 'boolean',
disabled: (instances: gdInitialInstance[]) => {
return instances.some(instance => !instance.isLocked());
},
getValue: (instance: gdInitialInstance) => instance.isSealed(),
setValue: (instance: gdInitialInstance, newValue: boolean) =>
instance.setSealed(newValue),
},
{
name: 'Z Order',
getLabel: () => i18n._(t`Z Order`),
valueType: 'number',
getValue: (instance: gdInitialInstance) => instance.getZOrder(),
setValue: (instance: gdInitialInstance, newValue: number) =>
instance.setZOrder(newValue),
},
{
name: 'Layer',
getLabel: () => i18n._(t`Layer`),
valueType: 'string',
getChoices: () => enumerateLayers(layout),
getValue: (instance: gdInitialInstance) => instance.getLayer(),
setValue: (instance: gdInitialInstance, newValue: string) =>
instance.setLayer(newValue),
},
{
name: 'Custom size',
getLabel: () => i18n._(t`Custom size`),
valueType: 'boolean',
getValue: (instance: gdInitialInstance) => instance.hasCustomSize(),
setValue: (instance: gdInitialInstance, newValue: boolean) => {
instance.setHasCustomSize(newValue);
forceUpdate();
},
},
{
name: 'custom-size-row',
type: 'row',
children: [
{
name: 'Width',
getLabel: () => i18n._(t`Width`),
valueType: 'number',
getValue: getInstanceWidth,
setValue: (instance: gdInitialInstance, newValue: number) => {
instance.setCustomWidth(Math.max(newValue, 0));
instance.setCustomHeight(getInstanceHeight(instance));
// This must be done after getInstanceHeight.
instance.setHasCustomSize(true);
forceUpdate();
},
},
{
name: 'Height',
getLabel: () => i18n._(t`Height`),
valueType: 'number',
getValue: getInstanceHeight,
setValue: (instance: gdInitialInstance, newValue: number) => {
instance.setCustomWidth(getInstanceWidth(instance));
instance.setCustomHeight(Math.max(newValue, 0));
// This must be done after getInstanceWidth.
instance.setHasCustomSize(true);
forceUpdate();
},
},
],
},
],
[
i18n,
getInstanceWidth,
getInstanceHeight,
onEditObjectByName,
layout,
forceUpdate,
]
const schemaFor3D: Schema = React.useMemo(
() =>
makeSchema({
i18n,
is3DInstance: true,
onGetInstanceSize,
onEditObjectByName,
layout,
forceUpdate,
}),
[i18n, onGetInstanceSize, onEditObjectByName, layout, forceUpdate]
);
// TODO: multiple instances support.
const instance = instances[0];
const { object, instanceSchema } = React.useMemo(
() => {
if (!instance) return {};
const associatedObjectName = instance.getObjectName();
const object = getObjectByName(project, layout, associatedObjectName);
// TODO: multiple instances support
const properties = instance.getCustomProperties(project, layout);
// TODO: Reorganize fields if any of the selected instances is 3D.
const is3DInstance = properties.has('z');
if (!object) return {};
// TODO (3D): Use 3D fields if any of the selected instances is 3D.
const is3DInstance = object.is3DObject();
const instanceSchemaForCustomProperties = propertiesMapToSchema(
properties,
(instance: gdInitialInstance) =>
@@ -240,14 +337,11 @@ const InstancePropertiesEditor = ({
return {
object,
instanceSchema: is3DInstance
? reorganizeSchemaFor3DInstance(
schema,
instanceSchemaForCustomProperties
)
: schema.concat(instanceSchemaForCustomProperties),
? schemaFor3D.concat(instanceSchemaForCustomProperties)
: schemaFor2D.concat(instanceSchemaForCustomProperties),
};
},
[project, layout, instance, schema]
[project, layout, instance, schemaFor2D, schemaFor3D]
);
if (!object || !instance || !instanceSchema) return null;

View File

@@ -9,7 +9,7 @@ import * as PIXI from 'pixi.js-legacy';
import * as THREE from 'three';
import { shouldBeHandledByPinch } from '../PinchHandler';
import { makeDoubleClickable } from './PixiDoubleClickEvent';
import Rectangle from '../../Utils/Rectangle';
import Rectangle from '../../Utils/Rectangle'; // TODO (3D): add support for zMin/zMax/depth.
import { rotatePolygon, type Polygon } from '../../Utils/PolygonHelper';
import Rendered3DInstance from '../../ObjectsRendering/Renderers/Rendered3DInstance';
const gd: libGDevelop = global.gd;
@@ -67,7 +67,6 @@ export default class LayerRenderer {
_threeGroup: THREE.Group | null = null;
_threeScene: THREE.Scene | null = null;
_threeCamera: THREE.PerspectiveCamera | null = null;
_threeCameraDirty: boolean = false;
// For a 2D+3D layer, the 2D rendering is done on the render texture
// and then must be displayed on a plane in the 3D world:
@@ -221,20 +220,25 @@ export default class LayerRenderer {
getUnrotatedInstanceSize = (instance: gdInitialInstance) => {
const renderedInstance = this.renderedInstances[instance.ptr];
if (instance.hasCustomSize())
return [
instance.getCustomWidth() ||
(renderedInstance ? renderedInstance.getDefaultWidth() : 0),
instance.getCustomHeight() ||
(renderedInstance ? renderedInstance.getDefaultHeight() : 0),
];
const hasCustomSize = instance.hasCustomSize();
const hasCustomDepth = instance.hasCustomDepth();
const width = hasCustomSize
? instance.getCustomWidth()
: renderedInstance
? renderedInstance.getDefaultWidth()
: 0;
const height = hasCustomSize
? instance.getCustomHeight()
: renderedInstance
? renderedInstance.getDefaultHeight()
: 0;
const depth = hasCustomDepth
? instance.getCustomDepth()
: renderedInstance
? renderedInstance.getDefaultDepth()
: 0;
return renderedInstance
? [
renderedInstance.getDefaultWidth(),
renderedInstance.getDefaultHeight(),
]
: [0, 0];
return [width, height, depth];
};
getUnrotatedInstanceAABB(
@@ -246,6 +250,7 @@ export default class LayerRenderer {
const top = this.getUnrotatedInstanceTop(instance);
const right = left + size[0];
const bottom = top + size[1];
// TODO (3D): add support for zMin/zMax/depth.
bounds.set({ left, top, right, bottom });
return bounds;
}
@@ -307,6 +312,7 @@ export default class LayerRenderer {
top = Math.min(top, rotatedRectangle[i][1]);
bottom = Math.max(bottom, rotatedRectangle[i][1]);
}
// TODO (3D): add support for zMin/zMax/depth.
bounds.set({ left, top, right, bottom });
return bounds;
}
@@ -478,12 +484,7 @@ export default class LayerRenderer {
this._threeGroup = new THREE.Group();
threeScene.add(this._threeGroup);
const threeCamera = new THREE.PerspectiveCamera(
this.layer.getCamera3DFieldOfView(),
1,
0.1,
2000
);
const threeCamera = new THREE.PerspectiveCamera(45, 1, 0.1, 2000);
threeCamera.rotation.order = 'ZYX';
this._threeCamera = threeCamera;
@@ -503,15 +504,6 @@ export default class LayerRenderer {
// to render, and that will be projected on a plane by Three.js
this._createPixiRenderTexture(pixiRenderer);
// Create the plane that will show this texture.
const threePlaneGeometry = new THREE.PlaneGeometry(1, 1);
this._threePlaneGeometry = threePlaneGeometry;
const threePlaneMaterial = new THREE.MeshBasicMaterial({
side: THREE.FrontSide,
transparent: true,
});
this._threePlaneMaterial = threePlaneMaterial;
// Create the texture to project on the plane.
// Use a buffer to create a "fake" DataTexture, just so the texture
// is considered initialized by Three.js.
@@ -532,7 +524,37 @@ export default class LayerRenderer {
threePlaneTexture.magFilter = filter;
threePlaneTexture.wrapS = THREE.ClampToEdgeWrapping;
threePlaneTexture.wrapT = THREE.ClampToEdgeWrapping;
threePlaneMaterial.map = threePlaneTexture;
// Create the plane that will show this texture.
const threePlaneGeometry = new THREE.PlaneGeometry(1, 1);
this._threePlaneGeometry = threePlaneGeometry;
// This disable the gamma correction done by THREE as PIXI is already doing it.
const noGammaCorrectionShader: THREE.ShaderMaterialParameters = {
vertexShader: `
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
fragmentShader: `
uniform sampler2D map;
varying vec2 vUv;
void main() {
vec4 texel = texture2D(map, vUv);
gl_FragColor = texel;
}
`,
uniforms: {
map: { value: this._threePlaneTexture },
},
side: THREE.FrontSide,
transparent: true,
};
const threePlaneMaterial = new THREE.ShaderMaterial(
noGammaCorrectionShader
);
this._threePlaneMaterial = threePlaneMaterial;
// Finally, create the mesh shown in the scene.
const threePlaneMesh = new THREE.Mesh(

View File

@@ -1,15 +1,15 @@
// @flow
import LayerRenderer from './LayerRenderer';
import ViewPosition from '../ViewPosition';
import BackgroundColor from '../BackgroundColor';
import * as PIXI from 'pixi.js-legacy';
import * as THREE from 'three';
import { rgbToHexNumber } from '../../Utils/ColorTransformer';
import Rectangle from '../../Utils/Rectangle';
export type InstanceMeasurer = {|
getInstanceAABB: (gdInitialInstance, Rectangle) => Rectangle,
getUnrotatedInstanceAABB: (gdInitialInstance, Rectangle) => Rectangle,
getUnrotatedInstanceSize: gdInitialInstance => [number, number],
getUnrotatedInstanceSize: gdInitialInstance => [number, number, number],
|};
export default class InstancesRenderer {
@@ -129,6 +129,8 @@ export default class InstancesRenderer {
bounds.top = instance.getY();
bounds.right = instance.getX();
bounds.bottom = instance.getY();
// TODO (3D): add support for zMin/zMax/depth.
return bounds;
}
@@ -138,7 +140,7 @@ export default class InstancesRenderer {
const layerName = instance.getLayer();
const layerRenderer = this.layersRenderers[layerName];
if (!layerRenderer) {
return [0, 0];
return [0, 0, 0];
}
return layerRenderer.getUnrotatedInstanceSize(instance);
@@ -158,7 +160,6 @@ export default class InstancesRenderer {
pixiRenderer: PIXI.Renderer,
threeRenderer: THREE.WebGLRenderer | null,
viewPosition: ViewPosition,
backgroundColor: BackgroundColor,
uiPixiContainer: PIXI.Container
) {
/** Useful to render the background color. */
@@ -169,6 +170,12 @@ export default class InstancesRenderer {
// And, out of caution, keep doing it for every frame.
if (threeRenderer) threeRenderer.resetState();
const backgroundColor = rgbToHexNumber(
this.layout.getBackgroundColorRed(),
this.layout.getBackgroundColorGreen(),
this.layout.getBackgroundColorBlue()
);
for (let i = 0; i < this.layout.getLayersCount(); i++) {
const layer = this.layout.getLayerAt(i);
const layerName = layer.getName();
@@ -210,6 +217,7 @@ export default class InstancesRenderer {
const threePlaneMesh = layerRenderer.getThreePlaneMesh();
if (threeCamera && threePlaneMesh) {
viewPosition.applyTransformationToThree(threeCamera, threePlaneMesh);
threeCamera.fov = layer.getCamera3DFieldOfView();
}
if (!threeRenderer) {
@@ -220,7 +228,7 @@ export default class InstancesRenderer {
pixiRenderer.reset();
// Render the background color.
backgroundColor.setBackgroundColorForPixi(pixiRenderer);
pixiRenderer.backgroundColor = backgroundColor;
pixiRenderer.backgroundAlpha = 1;
pixiRenderer.clear();
@@ -250,17 +258,23 @@ export default class InstancesRenderer {
pixiRenderer
);
threeRenderer.resetState();
// It's important to reset the internal WebGL state of PixiJS, then Three.js
// to ensure the 3D rendering is made properly by Three.js
pixiRenderer.reset();
threeRenderer.resetState();
if (isFirstRender) {
backgroundColor.setBackgroundColorForThree(
threeRenderer,
threeScene
);
// Render the background color.
threeRenderer.setClearColor(backgroundColor);
threeRenderer.resetState(); // Probably not needed, but keep it out of caution.
threeRenderer.clear();
threeScene.background = new THREE.Color(backgroundColor);
isFirstRender = false;
} else {
// It's important to set the background to null, as maybe the first rendered
// layer has changed and so the Three.js scene background must be reset.
threeScene.background = null;
}
// Clear the depth as each layer is independent and display on top of the previous one,
@@ -280,6 +294,13 @@ export default class InstancesRenderer {
}
pixiRenderer.render(uiPixiContainer);
if (threeRenderer) {
// It's important to reset the internal WebGL state of PixiJS, then Three.js
// to ensure the 3D rendering is made properly by Three.js
pixiRenderer.reset();
threeRenderer.resetState();
}
}
_updatePixiObjectsZOrder() {

View File

@@ -345,9 +345,18 @@ export default class InstancesResizer {
// prevent the user from modifying them manually in the inline fields.
selectedInstance.setX(Math.round(newX));
selectedInstance.setY(Math.round(newY));
// Also round the size, and set the default depth if necessary.
if (!selectedInstance.hasCustomDepth()) {
const defaultSize = this.instanceMeasurer.getUnrotatedInstanceSize(
selectedInstance
);
selectedInstance.setHasCustomDepth(true);
selectedInstance.setCustomDepth(defaultSize[2]);
}
selectedInstance.setHasCustomSize(true);
selectedInstance.setCustomWidth(Math.round(newWidth));
selectedInstance.setCustomHeight(Math.round(newHeight));
selectedInstance.setHasCustomSize(true);
}
}

View File

@@ -17,7 +17,6 @@ import InstancesMover from './InstancesMover';
import Grid from './Grid';
import WindowBorder from './WindowBorder';
import WindowMask from './WindowMask';
import BackgroundColor from './BackgroundColor';
import * as PIXI from 'pixi.js-legacy';
import * as THREE from 'three';
import FpsLimiter from './FpsLimiter';
@@ -72,6 +71,7 @@ export type InstancesEditorPropsWithoutSizeAndScroll = {|
selectedLayer: string,
initialInstances: gdInitialInstancesContainer,
instancesEditorSettings: InstancesEditorSettings,
isInstanceOf3DObject: gdInitialInstance => boolean,
onInstancesEditorSettingsMutated: (
instancesEditorSettings: InstancesEditorSettings
) => void,
@@ -127,7 +127,6 @@ export default class InstancesEditor extends Component<Props> {
statusBar: StatusBar;
pixiContainer: PIXI.Container;
backgroundArea: PIXI.Container;
backgroundColor: BackgroundColor;
instancesRenderer: InstancesRenderer;
viewPosition: ViewPosition;
longTouchHandler: LongTouchHandler;
@@ -270,7 +269,7 @@ export default class InstancesEditor extends Component<Props> {
}
});
this.pixiContainer = new PIXI.Container();
this.pixiContainer = new PIXI.Container(); // TODO (3D): rename this container.
this.backgroundArea = new PIXI.Container();
this.backgroundArea.hitArea = new PIXI.Rectangle(
@@ -399,9 +398,6 @@ export default class InstancesEditor extends Component<Props> {
this.pixiContainer.removeChild(this.statusBar.getPixiObject());
}
this.backgroundColor = new BackgroundColor({
layout: props.layout,
});
this.instancesRenderer = new InstancesRenderer({
project: props.project,
layout: props.layout,
@@ -435,6 +431,7 @@ export default class InstancesEditor extends Component<Props> {
this.highlightedInstance = new HighlightedInstance({
instanceMeasurer: this.instancesRenderer.getInstanceMeasurer(),
toCanvasCoordinates: this.viewPosition.toCanvasCoordinates,
isInstanceOf3DObject: this.props.isInstanceOf3DObject,
});
this.instancesResizer = new InstancesResizer({
instanceMeasurer: this.instancesRenderer.getInstanceMeasurer(),
@@ -1038,7 +1035,6 @@ export default class InstancesEditor extends Component<Props> {
this.pixiRenderer,
this.threeRenderer,
this.viewPosition,
this.backgroundColor,
this.pixiContainer
);
}
@@ -1059,7 +1055,9 @@ export default class InstancesEditor extends Component<Props> {
startPIXITicker();
};
getInstanceSize = (initialInstance: gdInitialInstance): [number, number] => {
getInstanceSize = (
initialInstance: gdInitialInstance
): [number, number, number] => {
return this.instancesRenderer
.getInstanceMeasurer()
.getUnrotatedInstanceSize(initialInstance);

View File

@@ -19,6 +19,7 @@ import {
CAMERA_PARALLAX_IN_APP_TUTORIAL_ID,
HEALTH_BAR_IN_APP_TUTORIAL_ID,
JOYSTICK_IN_APP_TUTORIAL_ID,
OBJECT_3D_IN_APP_TUTORIAL_ID,
guidedLessonsIds,
} from '../../../../Utils/GDevelopServices/InAppTutorial';
import MultiplierScore from './Icons/MultiplierScore';
@@ -33,6 +34,7 @@ import { ColumnStackLayout, LineStackLayout } from '../../../../UI/Layout';
import Text from '../../../../UI/Text';
import ColoredLinearProgress from '../../../../UI/ColoredLinearProgress';
import Trophy from '../../../../UI/CustomSvgIcons/Trophy';
import Object3D from './Icons/Object3D';
const getColumnsFromWidth = (width: WidthType) => {
switch (width) {
@@ -121,6 +123,18 @@ const GuidedLessons = ({ selectInAppTutorial }: Props) => {
durationInMinutes: 2,
renderImage: props => <HealthBar {...props} />,
},
{
id: OBJECT_3D_IN_APP_TUTORIAL_ID,
title: t`Add a 3D object`,
description: t`Learn how to add a 3D object to your game.`,
keyPoints: [
t`Add a 3D box`,
t`Add a behavior`,
t`Update the elevation of a 3D box`,
],
durationInMinutes: 2,
renderImage: props => <Object3D {...props} />,
},
{
id: CAMERA_PARALLAX_IN_APP_TUTORIAL_ID,
title: t`Improve background and camera`,

View File

@@ -0,0 +1,78 @@
import React from 'react';
import SvgIcon from '@material-ui/core/SvgIcon';
export default React.memo(props => (
<SvgIcon
{...props}
width="120"
height="138"
viewBox="0 0 120 138"
fill="none"
>
<path
d="M19.5 62.0972L6.00025 69.8906L19.5 77.684L32.9997 69.8906L19.5 62.0972Z"
fill="#FFFAF2"
/>
<path
d="M33 69.8364L19.7721 77.5087L20 93L33 85.5596V69.8364Z"
fill="#FFECD1"
/>
<path
d="M6.01367 69.8364L19.8503 77.5824L20 93L6.01367 85.5331V69.8364Z"
fill="#FFE1B4"
/>
<path
d="M47.665 40L23.985 53.6705L47.665 67.341L71.3451 53.6705L47.665 40Z"
fill="#FFEDE8"
/>
<path
d="M71.3447 53.5752L48.1556 67.182V94.6963L71.3447 81.1554V53.5752Z"
fill="#FFC2B4"
/>
<path
d="M24.0088 53.5752L48.2798 67.1625V94.6962L24.0088 81.1088V53.5752Z"
fill="#FF8569"
/>
<path
d="M94.6416 40.8213L76.2989 51.4105L94.6416 61.9997L112.984 51.4105L94.6416 40.8213Z"
fill="#FFFAF2"
/>
<path
d="M112.984 51.3369L95.0236 61.8395V83.2396L112.984 72.7007V51.3369Z"
fill="#FFBC57"
/>
<path
d="M76.3184 51.3369L95.1188 61.8617V83.1894L76.3184 72.6646V51.3369Z"
fill="#FB9600"
/>
<path d="M107.901 20H105.577V37.1051H107.901V20Z" fill="#F9A823" />
<path
d="M107.855 37.1051C111.115 37.1051 113.757 33.276 113.757 28.5526C113.757 23.8291 111.115 20 107.855 20C104.595 20 101.952 23.8291 101.952 28.5526C101.952 33.276 104.595 37.1051 107.855 37.1051Z"
fill="#F9A823"
/>
<path
d="M113.757 27.9018C109.043 24.8164 104.599 25.8711 102.695 24.5293C103.687 21.833 105.641 20 107.889 20C110.988 20 113.527 23.4831 113.757 27.9018Z"
fill="#F9A823"
/>
<path
d="M113.757 27.9018C109.043 24.8279 104.599 25.8787 102.695 24.5419C103.687 21.8556 102.695 19.7223 107.889 20.0295C110.988 20.0295 113.527 23.4996 113.757 27.9018Z"
fill="#F6C231"
/>
<path
d="M105.856 37.1051C109.09 37.1051 111.712 33.276 111.712 28.5526C111.712 23.8291 109.09 20 105.856 20C102.622 20 100 23.8291 100 28.5526C100 33.276 102.622 37.1051 105.856 37.1051Z"
fill="#FFF69B"
/>
<path
d="M105.763 37.1051C102.572 37.0137 100 33.2204 100 28.5536C100 23.8868 102.557 20.1105 105.737 20C105.15 25.6874 105.158 31.4195 105.763 37.1051Z"
fill="#FAF679"
/>
<path
d="M105.205 32.4548C104.592 32.4081 104.117 31.8174 104.089 31.1481C104.074 30.7507 103.997 30.3364 103.997 28.7173C103.997 27.3425 104.08 26.5117 104.089 26.2525C104.085 25.9415 104.203 25.6413 104.417 25.4153C104.518 25.3105 104.641 25.2293 104.777 25.1775C104.913 25.1256 105.059 25.1046 105.205 25.1157C105.519 25.1589 105.807 25.316 106.013 25.5571C106.219 25.7981 106.328 26.1061 106.321 26.4224C106.271 27.1704 106.239 27.963 106.239 28.798C106.239 29.5948 106.261 30.3513 106.306 31.0673C106.321 31.2854 106.284 31.5041 106.197 31.7048C105.983 32.1765 105.591 32.4846 105.205 32.4548Z"
fill="#F9A823"
/>
<path
d="M105.366 32.4557C104.841 32.409 104.418 31.8019 104.34 31.1353C104.279 30.6068 104.268 29.1739 104.281 28.7684C104.3 28.104 104.298 26.8706 104.361 26.2614C104.374 25.9488 104.491 25.6497 104.693 25.4123C104.773 25.3109 104.876 25.2308 104.993 25.179C105.111 25.1272 105.239 25.1052 105.366 25.1151C105.892 25.1618 106.321 25.7519 106.321 26.4206C106.273 27.1445 106.244 27.9066 106.241 28.7196C106.241 29.6345 106.269 30.5006 106.321 31.3115C106.223 31.9972 105.772 32.4833 105.366 32.4557Z"
fill="#FEBB2F"
/>
</SvgIcon>
));

View File

@@ -16,6 +16,7 @@ import {
CAMERA_PARALLAX_IN_APP_TUTORIAL_ID,
HEALTH_BAR_IN_APP_TUTORIAL_ID,
JOYSTICK_IN_APP_TUTORIAL_ID,
OBJECT_3D_IN_APP_TUTORIAL_ID,
isMiniTutorial,
} from '../../../../Utils/GDevelopServices/InAppTutorial';
@@ -131,6 +132,16 @@ const titleAndContentByKey = {
],
}),
},
[OBJECT_3D_IN_APP_TUTORIAL_ID]: {
title: <Trans>Let's add a 3D object to our game</Trans>,
content: getGuidedLessonContent({
learningKeys: [
<Trans>Add a 3D Box</Trans>,
<Trans>Add a behavior</Trans>,
<Trans>Update the elevation of a 3D box</Trans>,
],
}),
},
};
const StartInAppTutorialDialog = ({

View File

@@ -8,6 +8,7 @@ import { type ShortcutMap } from '../../KeyboardShortcuts/DefaultShortcuts';
import { type CommandName } from '../../CommandPalette/CommandsList';
import optionalRequire from '../../Utils/OptionalRequire';
import { findDefaultFolder } from '../../ProjectsStorage/LocalFileStorageProvider/LocalPathFinder';
import { isWebGLSupported } from '../../Utils/WebGL';
const electron = optionalRequire('electron');
const remote = optionalRequire('@electron/remote');
@@ -38,7 +39,8 @@ export type AlertMessageIdentifier =
| 'command-palette-shortcut'
| 'asset-installed-explanation'
| 'extension-installed-explanation'
| 'project-should-have-unique-package-name';
| 'project-should-have-unique-package-name'
| 'new-generate-project-from-prompt';
export type EditorMosaicName =
| 'scene-editor'
@@ -163,6 +165,10 @@ export const allAlertMessages: Array<{
<Trans>Project package names should not begin with com.example</Trans>
),
},
{
key: 'new-generate-project-from-prompt',
label: <Trans>New project generation from prompt warning</Trans>,
},
];
/**
@@ -322,7 +328,7 @@ export const initialPreferences = {
showCommunityExtensions: false,
showGetStartedSection: true,
showEventBasedObjectsEditor: false,
showObjectInstancesIn3D: false,
showObjectInstancesIn3D: isWebGLSupported(),
inAppTutorialsProgress: {},
newProjectsDefaultFolder: app ? findDefaultFolder(app) : '',
newProjectsDefaultStorageProviderName: 'Cloud',

View File

@@ -26,6 +26,7 @@ import { useResponsiveWindowWidth } from '../../UI/Reponsive/ResponsiveWindowMea
import { adaptAcceleratorString } from '../../UI/AcceleratorString';
import { getElectronAccelerator } from '../../KeyboardShortcuts';
import defaultShortcuts from '../../KeyboardShortcuts/DefaultShortcuts';
import AlertMessage from '../../UI/AlertMessage';
const electron = optionalRequire('electron');
type Props = {|
@@ -69,6 +70,10 @@ const PreferencesDialog = ({ i18n, onClose }: Props) => {
setUseShortcutToClosePreviewWindow,
} = React.useContext(PreferencesContext);
const initialShowObjectInstancesIn3D = React.useRef<boolean>(
values.showObjectInstancesIn3D
);
return (
<Dialog
title={<Trans>Preferences</Trans>}
@@ -350,18 +355,20 @@ const PreferencesDialog = ({ i18n, onClose }: Props) => {
</Trans>
}
/>
{/* TODO (3D) Remove development flag when the scene editor supports 3D display. */}
{Window.isDev() && (
<Toggle
onToggle={(e, check) => setShowObjectInstancesIn3D(check)}
toggled={values.showObjectInstancesIn3D}
labelPosition="right"
label={
<Trans>
Show objects in 3D in the scene editor (experimental)
</Trans>
}
/>
<Toggle
onToggle={(e, check) => setShowObjectInstancesIn3D(check)}
toggled={values.showObjectInstancesIn3D}
labelPosition="right"
label={<Trans>Show objects in 3D in the scene editor</Trans>}
/>
{initialShowObjectInstancesIn3D.current !==
values.showObjectInstancesIn3D && (
<AlertMessage kind="info">
<Trans>
For the 3D change to take effect, close and reopen all currently
opened scenes.
</Trans>
</AlertMessage>
)}
{electron && (
<>

View File

@@ -37,6 +37,7 @@ import { FullThemeProvider } from '../UI/Theme/FullThemeProvider';
type Props = {|
authentication: Authentication,
disableCheckForUpdates: boolean,
onlyAppStorePrivateAssetPacks?: boolean,
makeEventsFunctionCodeWriter: EventsFunctionCodeWriterCallbacks => ?EventsFunctionCodeWriter,
eventsFunctionsExtensionWriter: ?EventsFunctionsExtensionWriter,
eventsFunctionsExtensionOpener: ?EventsFunctionsExtensionOpener,
@@ -56,6 +57,7 @@ const Providers = ({
makeEventsFunctionCodeWriter,
eventsFunctionsExtensionWriter,
eventsFunctionsExtensionOpener,
onlyAppStorePrivateAssetPacks,
}: Props) => {
return (
<DragAndDropContextProvider>
@@ -92,7 +94,11 @@ const Providers = ({
>
<SubscriptionSuggestionProvider>
<CommandsContextProvider>
<AssetStoreStateProvider>
<AssetStoreStateProvider
onlyAppStorePrivateAssetPacks={
onlyAppStorePrivateAssetPacks
}
>
<ResourceStoreStateProvider>
<ExampleStoreStateProvider>
<ExtensionStoreStateProvider>

View File

@@ -83,15 +83,7 @@ import PreferencesContext, {
} from './Preferences/PreferencesContext';
import { getFunctionNameFromType } from '../EventsFunctionsExtensionsLoader';
import { type ExportDialogWithoutExportsProps } from '../Export/ExportDialog';
import CreateProjectDialog, {
createNewProjectFromTutorialTemplate,
createNewProjectWithDefaultLogin,
type NewProjectSetup,
} from '../ProjectCreation/CreateProjectDialog';
import {
createNewProjectFromExampleShortHeader,
createNewProject,
} from '../ProjectCreation/CreateProjectDialog';
import CreateProjectDialog from '../ProjectCreation/CreateProjectDialog';
import { getStartupTimesSummary } from '../Utils/StartupTimes';
import {
type StorageProvider,
@@ -161,11 +153,10 @@ import {
} from '../ProjectsStorage/ResourceFetcher';
import QuitInAppTutorialDialog from '../InAppTutorial/QuitInAppTutorialDialog';
import InAppTutorialContext from '../InAppTutorial/InAppTutorialContext';
import { useOpenInitialDialog } from '../Utils/UseOpenInitialDialog';
import useOpenInitialDialog from '../Utils/UseOpenInitialDialog';
import { type InAppTutorialOrchestratorInterface } from '../InAppTutorial/InAppTutorialOrchestrator';
import useInAppTutorialOrchestrator from '../InAppTutorial/useInAppTutorialOrchestrator';
import TabsTitlebar from './TabsTitlebar';
import { registerGame } from '../Utils/GDevelopServices/Game';
import RouterContext from './RouterContext';
import {
useStableUpToDateCallback,
@@ -180,6 +171,7 @@ import CustomDragLayer from '../UI/DragAndDrop/CustomDragLayer';
import CloudProjectRecoveryDialog from '../ProjectsStorage/CloudStorageProvider/CloudProjectRecoveryDialog';
import CloudProjectSaveChoiceDialog from '../ProjectsStorage/CloudStorageProvider/CloudProjectSaveChoiceDialog';
import { dataObjectToProps } from '../Utils/HTMLDataset';
import useCreateProject from '../Utils/UseCreateProject';
const GD_STARTUP_TIMES = global.GD_STARTUP_TIMES || [];
@@ -996,6 +988,48 @@ const MainFrame = (props: Props) => {
]
);
const {
createEmptyProject,
createProjectFromExample,
createProjectFromInAppTutorial,
createProjectWithLogin,
createProjectFromAIGeneration,
} = useCreateProject({
beforeCreatingProject: () => {
setIsProjectOpening(true);
},
getStorageProviderOperations,
afterCreatingProject: async ({ project, editorTabs, oldProjectId }) => {
setNewProjectSetupDialogOpen(false);
setSelectedExampleShortHeader(null);
await setState(state => ({ ...state, createDialogOpen: false }));
findLeaderboardsToReplace(project, oldProjectId);
openSceneOrProjectManager({
currentProject: project,
editorTabs: editorTabs,
});
setIsProjectClosedSoAvoidReloadingExtensions(false);
},
onError: () => {
setIsProjectClosedSoAvoidReloadingExtensions(true);
},
onSuccessOrError: () => {
// Stop the loading when we're successful or have failed.
setIsProjectOpening(false);
setIsLoadingProject(false);
setLoaderModalProgress(null, null);
},
loadFromProject,
openFromFileMetadata,
resourceMover,
onProjectSaved: fileMetadata => {
setState(state => ({
...state,
currentFileMetadata: fileMetadata,
}));
},
});
const closeApp = React.useCallback((): void => {
return Window.quit();
}, []);
@@ -2509,216 +2543,6 @@ const MainFrame = (props: Props) => {
}
};
const createProject = React.useCallback(
async (i18n: I18n, newProjectSetup: NewProjectSetup) => {
setIsProjectOpening(true);
// 4 cases when creating a project:
// - From an example
// - From an in-app tutorial
// - With the default login enabled
// - Empty project
const selectedInAppTutorialShortHeader = selectedInAppTutorialInfo
? getInAppTutorialShortHeader(selectedInAppTutorialInfo.tutorialId)
: null;
try {
const source = selectedExampleShortHeader
? await createNewProjectFromExampleShortHeader({
i18n,
exampleShortHeader: selectedExampleShortHeader,
})
: selectedInAppTutorialShortHeader &&
selectedInAppTutorialShortHeader.initialTemplateUrl
? await createNewProjectFromTutorialTemplate(
selectedInAppTutorialShortHeader.initialTemplateUrl
)
: newProjectSetup.allowPlayersToLogIn
? await createNewProjectWithDefaultLogin()
: await createNewProject();
if (!source) return; // New project creation aborted.
let state: ?State;
const sourceStorageProvider = source.storageProvider;
const sourceStorageProviderOperations = sourceStorageProvider
? getStorageProviderOperations(source.storageProvider)
: null;
if (source.project) {
state = await loadFromProject(source.project, null);
} else if (source.fileMetadata && sourceStorageProvider) {
state = await openFromFileMetadata(source.fileMetadata);
}
if (!state) {
throw new Error(
'Neither a project nor a file metadata to load was provided for the new project'
);
}
const { currentProject, editorTabs } = state;
if (!currentProject) {
throw new Error('The new project could not be opened.');
}
const oldProjectId = currentProject.getProjectUuid();
currentProject.resetProjectUuid();
currentProject.setVersion('1.0.0');
currentProject.getAuthorIds().clear();
currentProject.setAuthor('');
if (selectedExampleShortHeader) {
// Use the project settings of the example and add template slug to project
currentProject.setTemplateSlug(selectedExampleShortHeader.slug);
} else if (selectedInAppTutorialShortHeader) {
// Don't do anything, the project settings are already set by the tutorial.
} else {
// Use the project settings requested by the user
if (newProjectSetup.width && newProjectSetup.height) {
currentProject.setGameResolutionSize(
newProjectSetup.width,
newProjectSetup.height
);
}
if (newProjectSetup.orientation)
currentProject.setOrientation(newProjectSetup.orientation);
if (newProjectSetup.optimizeForPixelArt) {
currentProject.setPixelsRounding(true);
currentProject.setScaleMode('nearest');
}
}
if (!selectedInAppTutorialShortHeader) {
// If the project is a tutorial, keep the project name.
currentProject.setName(newProjectSetup.projectName || 'New game');
}
if (authenticatedUser.profile) {
// if the user is connected, try to register the game to avoid
// any gdevelop services to ask the user to register the game.
// (for instance, leaderboards, player authentication, ...)
try {
await registerGame(
authenticatedUser.getAuthorizationHeader,
authenticatedUser.profile.id,
{
gameId: currentProject.getProjectUuid(),
authorName:
currentProject.getAuthor() || 'Unspecified publisher',
gameName: currentProject.getName() || 'Untitled game',
templateSlug: currentProject.getTemplateSlug(),
}
);
} catch (error) {
// Do not prevent the user from opening the game if the registration failed.
console.error(
'Unable to register the game to the user profile, the game will not be listed in the user profile.',
error
);
}
}
const destinationStorageProviderOperations = getStorageProviderOperations(
newProjectSetup.storageProvider
);
const { onSaveProjectAs } = destinationStorageProviderOperations;
if (onSaveProjectAs) {
const { wasSaved, fileMetadata } = await onSaveProjectAs(
currentProject,
newProjectSetup.saveAsLocation,
{
onStartSaving: () => {
console.log('Start saving as the new project...');
},
onMoveResources: async ({ newFileMetadata }) => {
if (
!sourceStorageProvider ||
!sourceStorageProviderOperations ||
!source.fileMetadata
) {
console.log(
'No storage provider set or no previous FileMetadata (probably creating a blank project) - skipping resources copy.'
);
return;
}
await ensureResourcesAreMoved({
project: currentProject,
newFileMetadata,
newStorageProvider: newProjectSetup.storageProvider,
newStorageProviderOperations: destinationStorageProviderOperations,
oldFileMetadata: source.fileMetadata,
oldStorageProvider: sourceStorageProvider,
oldStorageProviderOperations: sourceStorageProviderOperations,
authenticatedUser,
});
},
}
);
if (wasSaved) {
setState(state => ({
...state,
currentFileMetadata: fileMetadata,
}));
unsavedChanges.sealUnsavedChanges();
if (newProjectSetup.storageProvider.internalName === 'LocalFile') {
preferences.setHasProjectOpened(true);
}
}
}
// We were able to load and then save the project. We can now close the dialog,
// open the project editors and check if leaderboards must be replaced.
setNewProjectSetupDialogOpen(false);
setSelectedExampleShortHeader(null);
await setState(state => ({ ...state, createDialogOpen: false }));
findLeaderboardsToReplace(currentProject, oldProjectId);
openSceneOrProjectManager({
currentProject: currentProject,
editorTabs: editorTabs,
});
setIsProjectClosedSoAvoidReloadingExtensions(false);
} catch (rawError) {
const { getWriteErrorMessage } = getStorageProviderOperations();
const errorMessage = getWriteErrorMessage
? getWriteErrorMessage(rawError)
: t`An error occurred when opening or saving the project. Try again later or choose another location to save the project to.`;
showErrorBox({
message: i18n._(errorMessage),
rawError,
errorId: 'project-creation-save-as-error',
});
setIsProjectClosedSoAvoidReloadingExtensions(true);
} finally {
// Stop the loading when we're successful or have failed.
setIsProjectOpening(false);
setIsLoadingProject(false);
setLoaderModalProgress(null, null);
}
},
[
ensureResourcesAreMoved,
findLeaderboardsToReplace,
getStorageProviderOperations,
openSceneOrProjectManager,
preferences,
setState,
unsavedChanges,
authenticatedUser,
loadFromProject,
openFromFileMetadata,
selectedExampleShortHeader,
getInAppTutorialShortHeader,
selectedInAppTutorialInfo,
]
);
const startSelectedTutorial = React.useCallback(
async (scenario: 'resume' | 'startOver' | 'start') => {
if (!selectedInAppTutorialInfo) return;
@@ -2775,11 +2599,14 @@ const MainFrame = (props: Props) => {
selectedInAppTutorialShortHeader.initialTemplateUrl;
if (initialTemplateUrl) {
try {
await createProject(i18n, {
storageProvider: emptyStorageProvider,
saveAsLocation: null,
// Remaining will be set by the template.
});
await createProjectFromInAppTutorial(
selectedInAppTutorialShortHeader.id,
{
storageProvider: emptyStorageProvider,
saveAsLocation: null,
// Remaining will be set by the template.
}
);
} catch (error) {
showErrorBox({
message: i18n._(
@@ -2812,7 +2639,7 @@ const MainFrame = (props: Props) => {
[
i18n,
getInAppTutorialShortHeader,
createProject,
createProjectFromInAppTutorial,
askToCloseProject,
startTutorial,
selectedInAppTutorialInfo,
@@ -3294,16 +3121,18 @@ const MainFrame = (props: Props) => {
{newProjectSetupDialogOpen && (
<NewProjectSetupDialog
authenticatedUser={authenticatedUser}
isOpening={isProjectOpening}
isOpeningProject={isProjectOpening}
onClose={() => setNewProjectSetupDialogOpen(false)}
onCreate={projectSettings => createProject(i18n, projectSettings)}
onCreateEmptyProject={createEmptyProject}
onCreateFromExample={createProjectFromExample}
onCreateWithLogin={createProjectWithLogin}
onCreateFromAIGeneration={async (generatedProject, projectSetup) => {
const projectFileUrl = generatedProject.fileUrl;
if (!projectFileUrl) return;
await createProjectFromAIGeneration(projectFileUrl, projectSetup);
}}
storageProviders={props.storageProviders}
isFromExample={!!selectedExampleShortHeader}
sourceExampleName={
selectedExampleShortHeader
? selectedExampleShortHeader.name
: undefined
}
selectedExampleShortHeader={selectedExampleShortHeader}
/>
)}
{cloudProjectFileMetadataToRecover && (

View File

@@ -129,6 +129,7 @@ const Cube3DEditor = ({
</InputAdornment>
</Tooltip>
}
id={`cube3d-object-${propertyName}`}
/>
</Column>
);
@@ -248,6 +249,7 @@ const Cube3DEditor = ({
value ? '1' : '0'
);
}}
id={`cube3d-object-${faceProperty.visibilityProperty}`}
/>
<Checkbox
checked={
@@ -264,6 +266,7 @@ const Cube3DEditor = ({
value ? '1' : '0'
);
}}
id={`cube3d-object-${faceProperty.resourceRepeatProperty}`}
/>
</ColumnStackLayout>
<ResourceSelectorWithThumbnail
@@ -279,6 +282,7 @@ const Cube3DEditor = ({
onChange={value =>
onChangeProperty(faceProperty.resourceNameProperty, value)
}
id={`cube3d-object-${faceProperty.resourceNameProperty}`}
/>
</ColumnStackLayout>
</React.Fragment>

View File

@@ -10,7 +10,7 @@ import SemiControlledTextField from '../../UI/SemiControlledTextField';
import useForceUpdate from '../../Utils/UseForceUpdate';
import ResourceSelector from '../../ResourcesList/ResourceSelector';
import Checkbox from '../../UI/Checkbox';
import { Column, Line } from '../../UI/Grid';
import { Column, Line, Spacer } from '../../UI/Grid';
import FormHelperText from '@material-ui/core/FormHelperText';
import InputAdornment from '@material-ui/core/InputAdornment';
import Tooltip from '@material-ui/core/Tooltip';
@@ -22,9 +22,41 @@ import { getMeasurementUnitShortLabel } from '../../PropertiesEditor/PropertiesM
import AlertMessage from '../../UI/AlertMessage';
import { type ResourceManagementProps } from '../../ResourcesList/ResourceSource';
import ResourcesLoader from '../../ResourcesLoader';
import IconButton from '../../UI/IconButton';
import RaisedButton from '../../UI/RaisedButton';
import FlatButton from '../../UI/FlatButton';
import { mapFor } from '../../Utils/MapFor';
import ScrollView, { type ScrollViewInterface } from '../../UI/ScrollView';
import { EmptyPlaceholder } from '../../UI/EmptyPlaceholder';
import Add from '../../UI/CustomSvgIcons/Add';
import Trash from '../../UI/CustomSvgIcons/Trash';
import { makeDragSourceAndDropTarget } from '../../UI/DragAndDrop/DragSourceAndDropTarget';
import { DragHandleIcon } from '../../UI/DragHandle';
import DropIndicator from '../../UI/SortableVirtualizedItemList/DropIndicator';
import GDevelopThemeContext from '../../UI/Theme/GDevelopThemeContext';
import PixiResourcesLoader from '../../ObjectsRendering/PixiResourcesLoader';
import useAlertDialog from '../../UI/Alert/useAlertDialog';
import { type GLTF } from 'three/examples/jsm/loaders/GLTFLoader';
const gd: libGDevelop = global.gd;
const DragSourceAndDropTarget = makeDragSourceAndDropTarget(
'model3d-animations-list'
);
const styles = {
rowContainer: {
display: 'flex',
flexDirection: 'column',
marginTop: 5,
},
rowContent: {
display: 'flex',
flex: 1,
alignItems: 'center',
},
};
export const hasLight = (layout: ?gd.Layout) => {
if (!layout) {
return true;
@@ -44,7 +76,8 @@ export const hasLight = (layout: ?gd.Layout) => {
const type = effect.getEffectType();
if (
type === 'Scene3D::AmbientLight' ||
type === 'Scene3D::DirectionalLight'
type === 'Scene3D::DirectionalLight' ||
type === 'Scene3D::HemisphereLight'
) {
return true;
}
@@ -142,6 +175,7 @@ type PropertyResourceSelectorProps = {|
propertyName: string,
project: gd.Project,
resourceManagementProps: ResourceManagementProps,
onChange: (value: string) => void,
|};
const PropertyResourceSelector = ({
@@ -149,6 +183,7 @@ const PropertyResourceSelector = ({
propertyName,
project,
resourceManagementProps,
onChange,
}: PropertyResourceSelectorProps) => {
const forceUpdate = useForceUpdate();
const { current: resourcesLoader } = React.useRef(ResourcesLoader);
@@ -157,9 +192,10 @@ const PropertyResourceSelector = ({
const onChangeProperty = React.useCallback(
(property: string, value: string) => {
objectConfiguration.updateProperty(property, value);
onChange(value);
forceUpdate();
},
[objectConfiguration, forceUpdate]
[objectConfiguration, onChange, forceUpdate]
);
const property = properties.get(propertyName);
@@ -183,11 +219,60 @@ const Model3DEditor = ({
objectConfiguration,
project,
layout,
object,
onSizeUpdated,
onObjectUpdated,
resourceManagementProps,
}: EditorProps) => {
const scrollView = React.useRef<?ScrollViewInterface>(null);
const [
justAddedAnimationName,
setJustAddedAnimationName,
] = React.useState<?string>(null);
const justAddedAnimationElement = React.useRef<?any>(null);
React.useEffect(
() => {
if (
scrollView.current &&
justAddedAnimationElement.current &&
justAddedAnimationName
) {
scrollView.current.scrollTo(justAddedAnimationElement.current);
setJustAddedAnimationName(null);
justAddedAnimationElement.current = null;
}
},
[justAddedAnimationName]
);
const { showAlert } = useAlertDialog();
const draggedAnimationIndex = React.useRef<number | null>(null);
const gdevelopTheme = React.useContext(GDevelopThemeContext);
const forceUpdate = useForceUpdate();
const model3DConfiguration = gd.asModel3DConfiguration(objectConfiguration);
const properties = objectConfiguration.getProperties();
const [nameErrors, setNameErrors] = React.useState<{ [number]: React.Node }>(
{}
);
const [model3D, setModel3D] = React.useState<?GLTF>(null);
const getModel3D = React.useCallback(
(modelResourceName: string) => {
PixiResourcesLoader.get3DModel(project, modelResourceName).then(
newModel3d => {
setModel3D(newModel3d);
}
);
},
[project]
);
getModel3D(properties.get('modelResourceName').getValue());
const onChangeProperty = React.useCallback(
(property: string, value: string) => {
objectConfiguration.updateProperty(property, value);
@@ -196,88 +281,493 @@ const Model3DEditor = ({
[objectConfiguration, forceUpdate]
);
const onChangeModelResourceName = React.useCallback(
(modelResourceName: string) => {
getModel3D(modelResourceName);
},
[getModel3D]
);
const scanNewAnimations = React.useCallback(
() => {
if (!model3D) {
return;
}
const animationSources = mapFor(
0,
model3DConfiguration.getAnimationsCount(),
animationIndex =>
model3DConfiguration.getAnimation(animationIndex).getSource()
);
let hasAddedAnimation = false;
for (const resourceAnimation of model3D.animations) {
if (animationSources.includes(resourceAnimation.name)) {
continue;
}
const newAnimationName = model3DConfiguration.hasAnimationNamed(
resourceAnimation.name
)
? ''
: resourceAnimation.name;
const newAnimation = new gd.Model3DAnimation();
newAnimation.setName(newAnimationName);
newAnimation.setSource(resourceAnimation.name);
model3DConfiguration.addAnimation(newAnimation);
newAnimation.delete();
hasAddedAnimation = true;
}
if (hasAddedAnimation) {
forceUpdate();
onSizeUpdated();
if (onObjectUpdated) onObjectUpdated();
// Scroll to the bottom of the list.
// Ideally, we'd wait for the list to be updated to scroll, but
// to simplify the code, we just wait a few ms for a new render
// to be done.
setTimeout(() => {
if (scrollView.current) {
scrollView.current.scrollToBottom();
}
}, 100); // A few ms is enough for a new render to be done.
} else {
showAlert({
title: t`No new animation`,
message: t`Every animation from the GLB file is already in the list.`,
});
}
},
[
forceUpdate,
model3D,
model3DConfiguration,
onObjectUpdated,
onSizeUpdated,
showAlert,
]
);
const addAnimation = React.useCallback(
() => {
const emptyAnimation = new gd.Model3DAnimation();
model3DConfiguration.addAnimation(emptyAnimation);
emptyAnimation.delete();
forceUpdate();
onSizeUpdated();
if (onObjectUpdated) onObjectUpdated();
// Scroll to the bottom of the list.
// Ideally, we'd wait for the list to be updated to scroll, but
// to simplify the code, we just wait a few ms for a new render
// to be done.
setTimeout(() => {
if (scrollView.current) {
scrollView.current.scrollToBottom();
}
}, 100); // A few ms is enough for a new render to be done.
},
[forceUpdate, onObjectUpdated, onSizeUpdated, model3DConfiguration]
);
const removeAnimation = React.useCallback(
animationIndex => {
model3DConfiguration.removeAnimation(animationIndex);
forceUpdate();
onSizeUpdated();
if (onObjectUpdated) onObjectUpdated();
},
[forceUpdate, onObjectUpdated, onSizeUpdated, model3DConfiguration]
);
const moveAnimation = React.useCallback(
(targetIndex: number) => {
const draggedIndex = draggedAnimationIndex.current;
if (draggedIndex === null) return;
model3DConfiguration.moveAnimation(
draggedIndex,
targetIndex > draggedIndex ? targetIndex - 1 : targetIndex
);
forceUpdate();
},
[model3DConfiguration, forceUpdate]
);
const changeAnimationName = React.useCallback(
(animationIndex, newName) => {
const currentName = model3DConfiguration
.getAnimation(animationIndex)
.getName();
if (currentName === newName) return;
const animation = model3DConfiguration.getAnimation(animationIndex);
if (nameErrors[animation.ptr]) {
const newNameErrors = { ...nameErrors };
delete newNameErrors[animation.ptr];
setNameErrors(newNameErrors);
}
if (newName !== '' && model3DConfiguration.hasAnimationNamed(newName)) {
setNameErrors({
...nameErrors,
[animation.ptr]: (
<Trans>The animation name {newName} is already taken</Trans>
),
});
return;
}
animation.setName(newName);
// TODO EBO Refactor event-based object events when an animation is renamed.
if (layout && object) {
gd.WholeProjectRefactorer.renameObjectAnimation(
project,
layout,
object,
currentName,
newName
);
}
forceUpdate();
if (onObjectUpdated) onObjectUpdated();
},
[
model3DConfiguration,
layout,
object,
forceUpdate,
onObjectUpdated,
nameErrors,
project,
]
);
const sourceSelectOptions = model3D
? model3D.animations.map(animation => {
return (
<SelectOption
key={animation.name}
value={animation.name}
label={animation.name}
shouldNotTranslate
/>
);
})
: [];
return (
<ColumnStackLayout noMargin>
<PropertyResourceSelector
objectConfiguration={objectConfiguration}
propertyName="modelResourceName"
project={project}
resourceManagementProps={resourceManagementProps}
/>
<Text size="block-title" noMargin>
<Trans>Default orientation</Trans>
</Text>
<ResponsiveLineStackLayout expand noColumnMargin>
<PropertyField
objectConfiguration={objectConfiguration}
propertyName="rotationX"
/>
<PropertyField
objectConfiguration={objectConfiguration}
propertyName="rotationY"
/>
<PropertyField
objectConfiguration={objectConfiguration}
propertyName="rotationZ"
/>
</ResponsiveLineStackLayout>
<Text size="block-title" noMargin>
<Trans>Default size</Trans>
</Text>
<ResponsiveLineStackLayout expand noColumnMargin>
<PropertyField
objectConfiguration={objectConfiguration}
propertyName="width"
/>
<PropertyField
objectConfiguration={objectConfiguration}
propertyName="height"
/>
<PropertyField
objectConfiguration={objectConfiguration}
propertyName="depth"
/>
</ResponsiveLineStackLayout>
<ColumnStackLayout noMargin expand>
<PropertyCheckbox
objectConfiguration={objectConfiguration}
propertyName="keepAspectRatio"
/>
<SelectField
value={properties.get('materialType').getValue()}
floatingLabelText={properties.get('materialType').getLabel()}
helperMarkdownText={properties.get('materialType').getDescription()}
onChange={(event, index, newValue) => {
onChangeProperty('materialType', newValue);
}}
<>
<ScrollView ref={scrollView}>
<ColumnStackLayout noMargin>
<PropertyResourceSelector
objectConfiguration={objectConfiguration}
propertyName="modelResourceName"
project={project}
resourceManagementProps={resourceManagementProps}
onChange={onChangeModelResourceName}
/>
<SelectField
value={properties.get('materialType').getValue()}
floatingLabelText={properties.get('materialType').getLabel()}
helperMarkdownText={properties.get('materialType').getDescription()}
onChange={(event, index, newValue) => {
onChangeProperty('materialType', newValue);
}}
>
<SelectOption
label={t`No lighting effect`}
value="Basic"
key="Basic"
/>
<SelectOption
label={t`Emit all ambient light`}
value="StandardWithoutMetalness"
key="StandardWithoutMetalness"
/>
<SelectOption
label={t`Keep model material`}
value="KeepOriginal"
key="KeepOriginal"
/>
</SelectField>
{properties.get('materialType').getValue() !== 'Basic' &&
!hasLight(layout) && (
<AlertMessage kind="error">
<Trans>
Make sure to set up a light in the effects of the layer or
chose "No lighting effect" - otherwise the object will appear
black.
</Trans>
</AlertMessage>
)}
<Text size="block-title" noMargin>
<Trans>Default orientation</Trans>
</Text>
<ResponsiveLineStackLayout expand noColumnMargin>
<PropertyField
objectConfiguration={objectConfiguration}
propertyName="rotationX"
/>
<PropertyField
objectConfiguration={objectConfiguration}
propertyName="rotationY"
/>
<PropertyField
objectConfiguration={objectConfiguration}
propertyName="rotationZ"
/>
</ResponsiveLineStackLayout>
<Text size="block-title" noMargin>
<Trans>Default size</Trans>
</Text>
<ResponsiveLineStackLayout expand noColumnMargin>
<PropertyField
objectConfiguration={objectConfiguration}
propertyName="width"
/>
<PropertyField
objectConfiguration={objectConfiguration}
propertyName="height"
/>
<PropertyField
objectConfiguration={objectConfiguration}
propertyName="depth"
/>
</ResponsiveLineStackLayout>
<PropertyCheckbox
objectConfiguration={objectConfiguration}
propertyName="keepAspectRatio"
/>
<Text size="block-title" noMargin>
<Trans>Points</Trans>
</Text>
<ResponsiveLineStackLayout expand noColumnMargin>
<SelectField
value={properties.get('originLocation').getValue()}
floatingLabelText={properties.get('originLocation').getLabel()}
helperMarkdownText={properties
.get('originLocation')
.getDescription()}
onChange={(event, index, newValue) => {
onChangeProperty('originLocation', newValue);
}}
fullWidth
>
<SelectOption
label={t`Model origin`}
value="ModelOrigin"
key="ModelOrigin"
/>
<SelectOption
label={t`Top-left corner`}
value="TopLeft"
key="TopLeftCorner"
/>
<SelectOption
label={t`Object center`}
value="ObjectCenter"
key="ObjectCenter"
/>
<SelectOption
label={t`Bottom center (on Z axis)`}
value="BottomCenterZ"
key="BottomCenterZ"
/>
<SelectOption
label={t`Bottom center (on Y axis)`}
value="BottomCenterY"
key="BottomCenterY"
/>
</SelectField>
<SelectField
value={properties.get('centerLocation').getValue()}
floatingLabelText={properties.get('centerLocation').getLabel()}
helperMarkdownText={properties
.get('centerLocation')
.getDescription()}
onChange={(event, index, newValue) => {
onChangeProperty('centerLocation', newValue);
}}
fullWidth
>
<SelectOption
label={t`Model origin`}
value="ModelOrigin"
key="ModelOrigin"
/>
<SelectOption
label={t`Object center`}
value="ObjectCenter"
key="ObjectCenter"
/>
<SelectOption
label={t`Bottom center (on Z axis)`}
value="BottomCenterZ"
key="BottomCenterZ"
/>
<SelectOption
label={t`Bottom center (on Y axis)`}
value="BottomCenterY"
key="BottomCenterY"
/>
</SelectField>
</ResponsiveLineStackLayout>
<Text size="block-title">Animations</Text>
<Column noMargin expand useFullHeight>
{model3DConfiguration.getAnimationsCount() === 0 ? (
<Column noMargin expand justifyContent="center">
<EmptyPlaceholder
title={<Trans>Add your first animation</Trans>}
description={
<Trans>Animations are a sequence of images.</Trans>
}
actionLabel={<Trans>Add an animation</Trans>}
helpPagePath="/objects/sprite"
tutorialId="intermediate-changing-animations"
onAction={addAnimation}
/>
</Column>
) : (
<React.Fragment>
{mapFor(
0,
model3DConfiguration.getAnimationsCount(),
animationIndex => {
const animation = model3DConfiguration.getAnimation(
animationIndex
);
const animationRef =
justAddedAnimationName === animation.getName()
? justAddedAnimationElement
: null;
return (
<DragSourceAndDropTarget
key={animation.ptr}
beginDrag={() => {
draggedAnimationIndex.current = animationIndex;
return {};
}}
canDrag={() => true}
canDrop={() => true}
drop={() => {
moveAnimation(animationIndex);
}}
>
{({
connectDragSource,
connectDropTarget,
isOver,
canDrop,
}) =>
connectDropTarget(
<div
key={animation.ptr}
style={styles.rowContainer}
>
{isOver && <DropIndicator canDrop={canDrop} />}
<div
ref={animationRef}
style={{
...styles.rowContent,
backgroundColor:
gdevelopTheme.list.itemsBackgroundColor,
}}
>
<Line noMargin expand alignItems="center">
{connectDragSource(
<span>
<Column>
<DragHandleIcon />
</Column>
</span>
)}
<Text noMargin noShrink>
<Trans>Animation #{animationIndex}</Trans>
</Text>
<Spacer />
<SemiControlledTextField
margin="none"
commitOnBlur
errorText={nameErrors[animation.ptr]}
translatableHintText={t`Optional animation name`}
value={animation.getName()}
onChange={text =>
changeAnimationName(animationIndex, text)
}
fullWidth
/>
<IconButton
size="small"
onClick={() =>
removeAnimation(animationIndex)
}
>
<Trash />
</IconButton>
</Line>
<Spacer />
</div>
<Spacer />
<ColumnStackLayout expand>
<SelectField
id="animation-source-field"
value={animation.getSource()}
onChange={(event, value) => {
animation.setSource(event.target.value);
forceUpdate();
}}
margin="dense"
fullWidth
floatingLabelText={
<Trans>GLB animation name</Trans>
}
translatableHintText={t`Choose an animation`}
>
{sourceSelectOptions}
</SelectField>
<Checkbox
label={<Trans>Loop</Trans>}
checked={animation.shouldLoop()}
onCheck={(e, checked) => {
animation.setShouldLoop(checked);
forceUpdate();
}}
/>
</ColumnStackLayout>
</div>
)
}
</DragSourceAndDropTarget>
);
}
)}
</React.Fragment>
)}
</Column>
</ColumnStackLayout>
</ScrollView>
<Column noMargin>
<ResponsiveLineStackLayout
justifyContent="space-between"
noColumnMargin
>
<SelectOption
label={t`No lighting effect`}
value="Basic"
key="Basic"
<FlatButton
label={<Trans>Scan missing animations</Trans>}
onClick={scanNewAnimations}
/>
<SelectOption
label={t`Emit all ambient light`}
value="StandardWithoutMetalness"
key="StandardWithoutMetalness"
<RaisedButton
label={<Trans>Add an animation</Trans>}
primary
onClick={addAnimation}
icon={<Add />}
/>
<SelectOption
label={t`Keep model material`}
value="KeepOriginal"
key="KeepOriginal"
/>
</SelectField>
{properties.get('materialType').getValue() !== 'Basic' &&
!hasLight(layout) && (
<AlertMessage kind="error">
<Trans>
Make sure to set up a light in the effects of the layer or chose
"No lighting effect" - otherwise the object will appear black.
</Trans>
</AlertMessage>
)}
</ColumnStackLayout>
</ColumnStackLayout>
</ResponsiveLineStackLayout>
</Column>
</>
);
};

View File

@@ -1,5 +1,5 @@
// @flow
import { Trans } from '@lingui/macro';
import { Trans, t } from '@lingui/macro';
import { type I18n as I18nType } from '@lingui/core';
import * as React from 'react';
import { SortableContainer, SortableElement } from 'react-sortable-hoc';
@@ -11,6 +11,8 @@ import {
copySpritePolygons,
allDirectionSpritesHaveSamePointsAs,
allDirectionSpritesHaveSameCollisionMasksAs,
deleteSpritesFromAnimation,
duplicateSpritesInAnimation,
} from './Utils/SpriteObjectHelper';
import ResourcesLoader from '../../../ResourcesLoader';
import {
@@ -32,6 +34,9 @@ import {
} from '../../../UI/Layout';
import { Column } from '../../../UI/Grid';
import Add from '../../../UI/CustomSvgIcons/Add';
import ContextMenu, {
type ContextMenuInterface,
} from '../../../UI/Menu/ContextMenu';
const gd: libGDevelop = global.gd;
const SPRITE_SIZE = 100; //TODO: Factor with Thumbnail
@@ -43,7 +48,7 @@ const styles = {
flex: 1,
},
thumbnailExtraStyle: {
marginLeft: 10,
marginLeft: 5,
},
spriteThumbnailImage: {
maxWidth: SPRITE_SIZE,
@@ -83,7 +88,7 @@ const SortableList = SortableContainer(
resourceManagementProps,
selectedSprites,
onSelectSprite,
onSpriteContextMenu,
onOpenSpriteContextMenu,
}) => {
const spritesCount = direction.getSpritesCount();
const hasMoreThanOneSprite = spritesCount > 1;
@@ -99,7 +104,7 @@ const SortableList = SortableContainer(
index={i}
isFirst={i === 0}
selected={!!selectedSprites[sprite.ptr]}
onContextMenu={(x, y) => onSpriteContextMenu(x, y, sprite)}
onContextMenu={(x, y) => onOpenSpriteContextMenu(x, y, sprite)}
onSelect={selected => onSelectSprite(sprite, selected)}
resourcesLoader={resourcesLoader}
project={project}
@@ -110,7 +115,7 @@ const SortableList = SortableContainer(
selectable
selected={!!selectedSprites[sprite.ptr]}
onSelect={selected => onSelectSprite(sprite, selected)}
onContextMenu={(x, y) => onSpriteContextMenu(x, y, sprite)}
onContextMenu={(x, y) => onOpenSpriteContextMenu(x, y, sprite)}
resourceName={sprite.getImageName()}
resourcesLoader={resourcesLoader}
project={project}
@@ -160,15 +165,11 @@ const removeExtensionFromFileName = (fileName: string) => {
};
type Props = {|
spriteConfiguration: gdSpriteObject,
direction: gdDirection,
project: gdProject,
resourcesLoader: typeof ResourcesLoader,
resourceManagementProps: ResourceManagementProps,
onSpriteContextMenu: (x: number, y: number, sprite: gdSprite) => void,
selectedSprites: {
[number]: boolean,
},
onSelectSprite: (sprite: gdSprite, selected: boolean) => void,
onReplaceByDirection: (newDirection: gdDirection) => void,
onSpriteUpdated?: () => void,
onChangeName: (newAnimationName: string) => void, // Used by piskel to set the name, if there is no name
@@ -177,13 +178,11 @@ type Props = {|
|};
const SpritesList = ({
spriteConfiguration,
direction,
project,
resourcesLoader,
resourceManagementProps,
onSpriteContextMenu,
selectedSprites,
onSelectSprite,
onReplaceByDirection,
onSpriteUpdated,
onChangeName,
@@ -191,16 +190,92 @@ const SpritesList = ({
animationName,
}: Props) => {
const [externalEditorOpened, setExternalEditorOpened] = React.useState(false);
// It's important to save the selected sprites in a ref, so that
// we can update the selection when a context menu is opened without relying on the state.
// Otherwise, the selection would be updated after the context menu is opened.
// Then, we need to ensure we trigger a force-update every time the selection changes.
const selectedSprites = React.useRef<{
[number]: boolean,
}>({});
const spriteContextMenu = React.useRef<?ContextMenuInterface>(null);
const forceUpdate = useForceUpdate();
const updateSelectionIndexesAfterMoveUp = React.useCallback(
(oldIndex: number, newIndex: number, wasMovedItemSelected: boolean) => {
for (let i = oldIndex; i <= newIndex; ++i) {
const spriteAtIndex = direction.getSprite(i);
if (i === newIndex) {
// If this is the new index of the moved sprite, we keep its selection status.
selectedSprites.current[spriteAtIndex.ptr] = wasMovedItemSelected;
} else {
// If moving up, the other sprites are going down, so their previous index was i+1.
const previousSpriteIndex = i + 1;
const previousSelectionStatus = !!selectedSprites.current[
direction.getSprite(previousSpriteIndex).ptr
];
selectedSprites.current[spriteAtIndex.ptr] = previousSelectionStatus;
}
}
},
[direction]
);
const updateSelectionIndexesAfterMoveDown = React.useCallback(
(oldIndex: number, newIndex: number, wasMovedItemSelected: boolean) => {
for (let i = oldIndex; i >= newIndex; --i) {
const spriteAtIndex = direction.getSprite(i);
if (i === newIndex) {
// If this is the new index of the moved sprite, we keep its selection status.
selectedSprites.current[spriteAtIndex.ptr] = wasMovedItemSelected;
} else {
// If moving down, the other sprites are going up, so their previous index was i-1.
const previousSpriteIndex = i - 1;
const previousSelectionStatus = !!selectedSprites.current[
direction.getSprite(previousSpriteIndex).ptr
];
selectedSprites.current[spriteAtIndex.ptr] = previousSelectionStatus;
}
}
},
[direction]
);
const onSortEnd = React.useCallback(
({ oldIndex, newIndex }: { oldIndex: number, newIndex: number }) => {
if (oldIndex === newIndex) return;
// We store the selection value of the moved sprite, as its pointer will
// be changed by the move.
const wasMovedItemSelected = !!selectedSprites.current[
direction.getSprite(oldIndex).ptr
];
direction.moveSprite(oldIndex, newIndex);
// When moving a sprite, the pointers are all shifted, so we need to
// update the selectedSprites map for the user not to lose their selection.
if (oldIndex < newIndex) {
updateSelectionIndexesAfterMoveUp(
oldIndex,
newIndex,
wasMovedItemSelected
);
} else {
updateSelectionIndexesAfterMoveDown(
oldIndex,
newIndex,
wasMovedItemSelected
);
}
forceUpdate();
onSpriteUpdated && onSpriteUpdated();
},
[direction, forceUpdate, onSpriteUpdated]
[
direction,
forceUpdate,
onSpriteUpdated,
updateSelectionIndexesAfterMoveDown,
updateSelectionIndexesAfterMoveUp,
]
);
const onAddSprite = React.useCallback(
@@ -355,6 +430,73 @@ const SpritesList = ({
]
);
const deleteSprites = React.useCallback(
() => {
const sprites = selectedSprites.current;
mapFor(0, spriteConfiguration.getAnimationsCount(), index => {
const animation = spriteConfiguration.getAnimation(index);
deleteSpritesFromAnimation(animation, sprites);
});
// Clear selection after deletion.
selectedSprites.current = {};
forceUpdate();
if (onSpriteUpdated) onSpriteUpdated();
},
[onSpriteUpdated, spriteConfiguration, forceUpdate]
);
const duplicateSprites = React.useCallback(
() => {
const sprites = selectedSprites.current;
mapFor(0, spriteConfiguration.getAnimationsCount(), index => {
const animation = spriteConfiguration.getAnimation(index);
duplicateSpritesInAnimation(animation, sprites);
});
// Clear selection after duplication.
selectedSprites.current = {};
forceUpdate();
if (onSpriteUpdated) onSpriteUpdated();
},
[onSpriteUpdated, spriteConfiguration, forceUpdate]
);
const selectSprite = React.useCallback(
(sprite, selected) => {
selectedSprites.current = {
...selectedSprites.current,
[sprite.ptr]: selected,
};
forceUpdate();
},
[forceUpdate]
);
const selectUniqueSprite = React.useCallback(
(sprite: gdSprite) => {
selectedSprites.current = {
[sprite.ptr]: true,
};
forceUpdate();
},
[forceUpdate]
);
const openSpriteContextMenu = React.useCallback(
(x, y, sprite) => {
// If the sprite is not selected, select only it.
if (!selectedSprites.current[sprite.ptr]) {
selectUniqueSprite(sprite);
}
// Otherwise, keep the selection as is.
if (spriteContextMenu.current) {
spriteContextMenu.current.open(x, y);
}
},
[selectUniqueSprite]
);
const storageProvider = resourceManagementProps.getStorageProvider();
const resourceSources = resourceManagementProps.resourceSources
.filter(source => source.kind === 'image')
@@ -385,13 +527,26 @@ const SpritesList = ({
onSortEnd={onSortEnd}
onAddSprite={onAddSprite}
resourceManagementProps={resourceManagementProps}
selectedSprites={selectedSprites}
onSelectSprite={onSelectSprite}
onSpriteContextMenu={onSpriteContextMenu}
selectedSprites={selectedSprites.current}
onSelectSprite={selectSprite}
onOpenSpriteContextMenu={openSpriteContextMenu}
helperClass="sortable-helper"
lockAxis="x"
axis="x"
/>
<ContextMenu
ref={spriteContextMenu}
buildMenuTemplate={(i18n: I18nType) => [
{
label: i18n._(t`Delete selection`),
click: deleteSprites,
},
{
label: i18n._(t`Duplicate selection`),
click: duplicateSprites,
},
]}
/>
<Column noMargin>
<RaisedButtonWithSplitMenu
onClick={() => {

View File

@@ -16,18 +16,11 @@ import Dialog from '../../../UI/Dialog';
import HelpButton from '../../../UI/HelpButton';
import MiniToolbar, { MiniToolbarText } from '../../../UI/MiniToolbar';
import DragHandle from '../../../UI/DragHandle';
import ContextMenu, {
type ContextMenuInterface,
} from '../../../UI/Menu/ContextMenu';
import { showWarningBox } from '../../../UI/Messages/MessageBox';
import ResourcesLoader from '../../../ResourcesLoader';
import PointsEditor from './PointsEditor';
import CollisionMasksEditor from './CollisionMasksEditor';
import Window from '../../../Utils/Window';
import {
deleteSpritesFromAnimation,
duplicateSpritesInAnimation,
} from './Utils/SpriteObjectHelper';
import { type EditorProps } from '../EditorProps.flow';
import { type ResourceManagementProps } from '../../../ResourcesList/ResourceSource';
import { Column } from '../../../UI/Grid';
@@ -54,17 +47,13 @@ const styles = {
};
type AnimationProps = {|
spriteConfiguration: gdSpriteObject,
animation: gdAnimation,
id: number,
project: gdProject,
resourceManagementProps: ResourceManagementProps,
onRemove: () => void,
resourcesLoader: typeof ResourcesLoader,
onSpriteContextMenu: (x: number, y: number, sprite: gdSprite) => void,
selectedSprites: {
[number]: boolean,
},
onSelectSprite: (sprite: gdSprite, selected: boolean) => void,
onReplaceDirection: (
directionIndex: number,
newDirection: gdDirection
@@ -76,15 +65,13 @@ type AnimationProps = {|
|};
const Animation = ({
spriteConfiguration,
animation,
id,
project,
onRemove,
resourceManagementProps,
resourcesLoader,
onSpriteContextMenu,
selectedSprites,
onSelectSprite,
onReplaceDirection,
objectName,
onChangeName,
@@ -125,14 +112,12 @@ const Animation = ({
const direction = animation.getDirection(i);
return (
<SpritesList
spriteConfiguration={spriteConfiguration}
direction={direction}
key={i}
project={project}
resourcesLoader={resourcesLoader}
resourceManagementProps={resourceManagementProps}
onSpriteContextMenu={onSpriteContextMenu}
selectedSprites={selectedSprites}
onSelectSprite={onSelectSprite}
onReplaceByDirection={newDirection =>
onReplaceDirection(i, newDirection)
}
@@ -161,9 +146,6 @@ const SortableAnimationsList = SortableContainer(
resourcesLoader,
resourceManagementProps,
extraBottomTools,
onSpriteContextMenu,
selectedSprites,
onSelectSprite,
onReplaceDirection,
isAnimationListLocked,
onSpriteUpdated,
@@ -178,6 +160,7 @@ const SortableAnimationsList = SortableContainer(
const animation = spriteConfiguration.getAnimation(i);
return (
<SortableAnimation
spriteConfiguration={spriteConfiguration}
isAnimationListLocked={isAnimationListLocked}
key={i}
index={i}
@@ -188,9 +171,6 @@ const SortableAnimationsList = SortableContainer(
resourceManagementProps={resourceManagementProps}
onRemove={() => onRemoveAnimation(i)}
onChangeName={newName => onChangeAnimationName(i, newName)}
onSpriteContextMenu={onSpriteContextMenu}
selectedSprites={selectedSprites}
onSelectSprite={onSelectSprite}
onReplaceDirection={(directionId, newDirection) =>
onReplaceDirection(i, directionId, newDirection)
}
@@ -231,11 +211,6 @@ const AnimationsListContainer = ({
onObjectUpdated,
isAnimationListLocked,
}: AnimationsListContainerProps) => {
const [selectedSprites, setSelectedSprites] = React.useState<{
[number]: boolean,
}>({});
const spriteContextMenu = React.useRef<?ContextMenuInterface>(null);
const scrollView = React.useRef<?ScrollViewInterface>(null);
const forceUpdate = useForceUpdate();
@@ -336,52 +311,6 @@ const AnimationsListContainer = ({
[forceUpdate, layout, object, onObjectUpdated, project, spriteConfiguration]
);
const deleteSelection = React.useCallback(
() => {
mapFor(0, spriteConfiguration.getAnimationsCount(), index => {
const animation = spriteConfiguration.getAnimation(index);
deleteSpritesFromAnimation(animation, selectedSprites);
});
setSelectedSprites({});
if (onObjectUpdated) onObjectUpdated();
},
[onObjectUpdated, selectedSprites, spriteConfiguration]
);
const duplicateSelection = React.useCallback(
() => {
mapFor(0, spriteConfiguration.getAnimationsCount(), index => {
const animation = spriteConfiguration.getAnimation(index);
duplicateSpritesInAnimation(animation, selectedSprites);
});
setSelectedSprites({});
if (onObjectUpdated) onObjectUpdated();
},
[onObjectUpdated, selectedSprites, spriteConfiguration]
);
const selectSprite = React.useCallback(
(sprite, selected) => {
setSelectedSprites({
...selectedSprites,
[sprite.ptr]: selected,
});
},
[selectedSprites]
);
const openSpriteContextMenu = React.useCallback(
(x, y, sprite, index) => {
selectSprite(sprite, true);
if (spriteContextMenu.current) {
spriteContextMenu.current.open(x, y);
}
},
[selectSprite]
);
const replaceDirection = React.useCallback(
(animationId, directionId, newDirection) => {
spriteConfiguration
@@ -425,9 +354,6 @@ const AnimationsListContainer = ({
}
onRemoveAnimation={index => removeAnimation(index, i18n)}
onReplaceDirection={replaceDirection}
onSpriteContextMenu={openSpriteContextMenu}
selectedSprites={selectedSprites}
onSelectSprite={selectSprite}
resourcesLoader={resourcesLoader}
resourceManagementProps={resourceManagementProps}
useDragHandle
@@ -452,19 +378,6 @@ const AnimationsListContainer = ({
)}
</ResponsiveLineStackLayout>
</Column>
<ContextMenu
ref={spriteContextMenu}
buildMenuTemplate={(i18n: I18nType) => [
{
label: i18n._(t`Delete selection`),
click: deleteSelection,
},
{
label: i18n._(t`Duplicate selection`),
click: duplicateSelection,
},
]}
/>
</React.Fragment>
)}
</Column>

View File

@@ -14,6 +14,7 @@ import RenderedInstance from './Renderers/RenderedInstance';
import Rendered3DInstance from './Renderers/Rendered3DInstance';
import * as PIXI from 'pixi.js-legacy';
import * as THREE from 'three';
import * as SkeletonUtils from 'three/examples/jsm/utils/SkeletonUtils';
import optionalRequire from '../Utils/OptionalRequire';
import { rgbOrHexToHexNumber } from '../Utils/ColorTransformer';
const path = optionalRequire('path');
@@ -253,6 +254,9 @@ const ObjectsRenderingService = {
gd, // Expose gd so that it can be used by renderers
PIXI, // Expose PIXI so that it can be used by renderers
THREE, // Expose THREE so that it can be used by renderers
THREE_ADDONS: {
SkeletonUtils,
}, // Expose THREE so that it can be used by renderers
RenderedInstance, // Expose the base class for renderers so that it can be used by renderers
Rendered3DInstance, // Expose the base class for 3D renderers so that it can be used by renderers
};

View File

@@ -3,7 +3,8 @@ import slugs from 'slugs';
import axios from 'axios';
import * as PIXI from 'pixi.js-legacy';
import * as THREE from 'three';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
import { GLTFLoader, GLTF } from 'three/examples/jsm/loaders/GLTFLoader';
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader';
import ResourcesLoader from '../ResourcesLoader';
import { loadFontFace } from '../Utils/FontFaceLoader';
import { checkIfCredentialsRequired } from '../Utils/CrossOrigin';
@@ -16,10 +17,41 @@ const invalidTexture = PIXI.Texture.from('res/error48.png');
const loadedThreeTextures = {};
const loadedThreeMaterials = {};
const loaded3DModels = {};
const invalidModel = new THREE.Mesh(
new THREE.BoxGeometry(1, 1, 1),
new THREE.MeshBasicMaterial({ color: '#ff00ff' })
);
const createInvalidModel = (): GLTF => {
/**
* The invalid model is a box with magenta (#ff00ff) faces, to be
* easily spotted if rendered on screen.
*/
const group = new THREE.Group();
group.add(
new THREE.Mesh(
new THREE.BoxGeometry(1, 1, 1),
new THREE.MeshBasicMaterial({ color: '#ff00ff' })
)
);
return {
scene: group,
animations: [],
cameras: [],
scenes: [],
asset: {},
userData: {},
parser: null,
};
};
const invalidModel: GLTF = createInvalidModel();
let gltfLoader = null;
const getOrCreateGltfLoader = () => {
if (!gltfLoader) {
gltfLoader = new GLTFLoader();
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath('./external/draco/gltf/');
gltfLoader.setDRACOLoader(dracoLoader);
}
return gltfLoader;
};
const determineCrossOrigin = (url: string) => {
// Any resource stored on the GDevelop Cloud buckets needs the "credentials" of the user,
@@ -54,6 +86,38 @@ const applyThreeTextureSettings = (
}
};
const convertToBasicMaterial = (
material: THREE.Material
): THREE.MeshBasicMaterial => {
const basicMaterial = new THREE.MeshBasicMaterial();
if (material.color) {
basicMaterial.color = material.color;
}
if (material.map) {
basicMaterial.map = material.map;
}
return basicMaterial;
};
const setBasicMaterialTo = (node: THREE.Object3D<THREE.Event>): void => {
const mesh = (node: THREE.Mesh);
if (!mesh.material) {
return;
}
if (Array.isArray(mesh.material)) {
for (let index = 0; index < mesh.material.length; index++) {
mesh.material[index] = convertToBasicMaterial(mesh.material[index]);
}
} else {
mesh.material = convertToBasicMaterial(mesh.material);
}
};
const traverseToSetBasicMaterialFromMeshes = (
node: THREE.Object3D<THREE.Event>
) => node.traverse(setBasicMaterialTo);
/**
* Expose functions to load PIXI textures or fonts, given the names of
* resources and a gd.Project.
@@ -196,6 +260,7 @@ export default class PixiResourcesLoader {
threeTexture.minFilter = THREE.LinearFilter;
threeTexture.wrapS = THREE.RepeatWrapping;
threeTexture.wrapT = THREE.RepeatWrapping;
threeTexture.colorSpace = THREE.SRGBColorSpace;
threeTexture.needsUpdate = true;
const resource = project.getResourcesManager().getResource(resourceName);
@@ -242,7 +307,7 @@ export default class PixiResourcesLoader {
static get3DModel(
project: gdProject,
resourceName: string
): Promise<THREE.Object3D> {
): Promise<THREE.THREE_ADDONS.GLTF> {
const loaded3DModel = loaded3DModels[resourceName];
if (loaded3DModel) return Promise.resolve(loaded3DModel);
@@ -256,17 +321,17 @@ export default class PixiResourcesLoader {
isResourceForPixi: true,
});
const loader = new GLTFLoader();
loader.withCredentials = checkIfCredentialsRequired(url);
const gltfLoader = getOrCreateGltfLoader();
gltfLoader.withCredentials = checkIfCredentialsRequired(url);
// TODO Cache promises that are not yet resolved to void `load` being
// called more than once for the same resource.
return new Promise((resolve, reject) => {
loader.load(
gltfLoader.load(
url,
gltf => {
this._replaceMaterials(gltf.scene);
loaded3DModels[resourceName] = gltf.scene;
resolve(gltf.scene);
traverseToSetBasicMaterialFromMeshes(gltf.scene);
loaded3DModels[resourceName] = gltf;
resolve(gltf);
},
undefined,
error => {
@@ -276,29 +341,6 @@ export default class PixiResourcesLoader {
});
}
/**
* Replace materials with `MeshBasicMaterial` as lights are not yet supported.
*/
static _replaceMaterials(object3D: THREE.Object3D) {
object3D.traverse(node => {
if (node.type === 'Mesh') {
const mesh: THREE.Mesh = node;
const basicMaterial = new THREE.MeshBasicMaterial();
//@ts-ignore
if (mesh.material.color) {
//@ts-ignore
basicMaterial.color = mesh.material.color;
}
//@ts-ignore
if (mesh.material.map) {
//@ts-ignore
basicMaterial.map = mesh.material.map;
}
mesh.material = basicMaterial;
}
});
}
/**
* Return the PIXI video texture represented by the given resource.
* If not loaded, it will load it.

View File

@@ -379,10 +379,22 @@ export class ChildInstance {
return this.y;
}
getZ() {
return 0;
}
getAngle() {
return 0;
}
getRotationX() {
return 0;
}
getRotationY() {
return 0;
}
setObjectName(name: string) {}
getObjectName() {
@@ -393,8 +405,14 @@ export class ChildInstance {
setY(y: number) {}
setZ(z: number) {}
setAngle(angle: number) {}
setRotationX(angle: number) {}
setRotationY(angle: number) {}
isLocked() {
return false;
}
@@ -427,6 +445,9 @@ export class ChildInstance {
return this._hasCustomSize;
}
hasCustomDepth() {
return false;
}
setCustomWidth(width: number) {
this._customWidth = width;
this._hasCustomSize = true;
@@ -445,6 +466,14 @@ export class ChildInstance {
return this._customHeight;
}
setCustomDepth(depth: number) {
// Not implemented.
}
getCustomDepth() {
return 0;
}
resetPersistentUuid() {
return this;
}

View File

@@ -94,26 +94,25 @@ export default class Rendered3DInstance {
return this.getHeight() / 2;
}
getCustomWidth(): number {
return this._instance.getCustomWidth() || this.getDefaultWidth();
}
getCustomHeight(): number {
return this._instance.getCustomHeight() || this.getDefaultHeight();
}
getWidth(): number {
return this._instance.hasCustomSize()
? this.getCustomWidth()
? this._instance.getCustomWidth()
: this.getDefaultWidth();
}
getHeight(): number {
return this._instance.hasCustomSize()
? this.getCustomHeight()
? this._instance.getCustomHeight()
: this.getDefaultHeight();
}
getDepth(): number {
// For compatibility, a custom depth can be used, without necessarily a custom width/height.
return this._instance.hasCustomDepth()
? this._instance.getCustomDepth()
: this.getDefaultDepth();
}
/**
* Return the width of the instance when the instance doesn't have a custom size.
*/
@@ -127,4 +126,11 @@ export default class Rendered3DInstance {
getDefaultHeight() {
return 32;
}
/**
* Return the depth of the instance when the instance doesn't have a custom size.
*/
getDefaultDepth() {
return 32;
}
}

Some files were not shown because too many files have changed in this diff Show More