Compare commits

..

108 Commits

Author SHA1 Message Date
AlexandreS
b43d5ec425 Improve copy regarding faces orientation of the 3D box (#5432) 2023-06-22 09:27:06 +02:00
Arthur Pacaud
3c43f28966 Add extension to support Steamworks integration of games made with GDevelop (#5401) 2023-06-22 09:16:20 +02:00
Clément Pasteau
a3f7c5782e Fix creating project properties from template to be set properly (#5436) 2023-06-22 09:14:46 +02:00
AlexandreS
8dec6dfa28 Bump newIDE version (#5433) 2023-06-22 09:13:01 +02:00
D8H
4eb49bdeb2 Use the object type for 3D model assets (#5431)
* Don't show in changelog
2023-06-21 20:14:46 +02:00
Clément Pasteau
813cadbd6e Improve prompt generator to hint on game type for better results (#5430)
Do not show in changelog
2023-06-21 16:04:01 +02:00
github-actions[bot]
58dd2c1a7b Update translations [skip ci] (#5428)
Co-authored-by: D8H <D8H@users.noreply.github.com>
2023-06-21 15:57:23 +02:00
AlexandreS
104c27ebc8 Improve guided lessons framework (#5426)
- Improve tooltip placement
- Fix z order setting step sometimes blocked
2023-06-21 14:48:39 +02:00
D8H
318099504c Handle 3D models in the asset store (#5427) 2023-06-21 14:46:34 +02:00
supertree-wook
8851be03a3 Fix typos here and there (#5253)
Only show in developer changelog
2023-06-21 12:41:17 +02:00
github-actions[bot]
a9a126ab0d Update translations [skip ci] (#5422)
Co-authored-by: AlexandreSi <AlexandreSi@users.noreply.github.com>
2023-06-21 11:20:33 +02:00
AlexandreS
19e46fedc8 Fix scroll on autocompletion displayer on mobile (#5425) 2023-06-21 11:12:07 +02:00
Arthur Pacaud
ff987a0751 Suggest the authenticated user's username by default in inputs showing usernames (#5423) 2023-06-21 08:21:44 +02:00
AlexandreS
0c0ab65b1a Display more info on missing action/condition placeholder (#5415) 2023-06-20 09:43:18 +02:00
AlexandreS
52a5908d7e Rename 3D editor preference to enable 3D editor for everyone (#5421) 2023-06-20 09:37:22 +02:00
github-actions[bot]
49926a89a2 Update translations [skip ci] (#5417)
Co-authored-by: AlexandreSi <AlexandreSi@users.noreply.github.com>
2023-06-20 09:25:08 +02:00
D8H
b63b91f33d Fix out-of-limits dragging of animation in sprite editor (#5413)
Also:
- Show duplicated animation name errors directly on the field instead of a popup

---------

Co-authored-by: AlexandreSi <32449369+AlexandreSi@users.noreply.github.com>
2023-06-19 17:13:26 +02:00
supertree-wook
67ea361416 Remove duplicate cache registration code (#5411) 2023-06-16 17:37:58 +02:00
github-actions[bot]
3b9078c6b3 Update translations [skip ci] (#5408)
Co-authored-by: D8H <D8H@users.noreply.github.com>
2023-06-16 14:12:34 +02:00
D8H
3d9e3f997e Clear 3D animation name errors when they are moved (#5416) 2023-06-16 12:27:40 +02:00
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
AlexandreS
a7760e975a Bump newIDE version (#5353) 2023-05-25 16:41:28 +02:00
AlexandreS
e2397cb0a4 Make sure dialogs are displayed full screen on mobiles landscape-oriented (#5350) 2023-05-25 14:54:45 +02:00
Aurélien Vivet
24ff670886 Make more visible the actions and condition to Save and Load the of a game.
- Replace the actions and conditions "Write a value" by "Save a value", and "Read a value" by "Load a value".
2023-05-25 13:39:06 +01:00
github-actions[bot]
d24b7497c9 Update translations [skip ci] (#5341) 2023-05-25 14:29:25 +02:00
supertree-wook
0ecaa342f9 Fix and improve GithHb action to see translations with coverage deltas (#5348) 2023-05-25 12:59:14 +02:00
D8H
141ac87c6e Convert animation list to functional component (#5342)
* Don't show in changelog
2023-05-25 09:35:43 +02:00
Aurélien Vivet
057e6dcfed Fix typos (#5347) 2023-05-24 22:41:49 +02:00
D8H
9c69c90a66 Fix object instance dimension values editor (#5346)
* Also fix default value for the "3D world top" property of directional lights
2023-05-24 21:21:35 +02:00
AlexandreS
4df6ecc654 Fix 3D model object not showing in preview when working on the web or mobile app (#5344) 2023-05-24 20:01:59 +02:00
AlexandreS
c106d05098 Use the native selector to select an object on mobile (#5343)
This should fix issues happening on mobile where the keyboard opens when one wants to select an object from the parameter of an event
2023-05-24 17:26:27 +02:00
Clément Pasteau
f3f426e949 Update icon for mac (#5340)
Don't show in changelog
2023-05-23 16:47:25 +02:00
github-actions[bot]
52d0ed1217 Update translations [skip ci] (#5337) 2023-05-23 16:04:49 +02:00
AlexandreS
511138269d Bump newIDE version (#5339) 2023-05-23 15:46:23 +02:00
AlexandreS
22dda6194c Add QR code when doing a network preview to facilitate accessing the testing url (#5338) 2023-05-23 15:45:20 +02:00
github-actions[bot]
aa2ef39f32 Update translations [skip ci] (#5335)
Co-authored-by: D8H <D8H@users.noreply.github.com>
2023-05-23 14:23:06 +02:00
Clément Pasteau
a744b88c5c Rework objectGroupsContainer to contain references, avoiding possible wrong memory accesses (#5329)
Do not show in changelog
2023-05-23 14:22:39 +02:00
D8H
2847601fc5 Add an alert when event-functions only call themselves (#5324) 2023-05-23 12:09:56 +02:00
D8H
bfbdf1cd3f Fix outline style when drawing a path with the shape painter (#5336) 2023-05-23 10:33:53 +02:00
Clément Pasteau
ed1694a818 Update GitHub actions (#5279)
Only show in developer changelog

Co-authored-by: supertree-wook <phk09242@supertree.co>
2023-05-23 10:24:52 +02:00
AlexandreS
0ee121c014 Add a few missing things following 3D release (#5328)
- Restore Debug draw action in games with 3D
- Fix inspector not working on 3D objects with Z = 0
- Use magenta box when no model is specified for 3D model objects
- Add missing default names for 2 new 3D objects
2023-05-23 10:21:09 +02:00
D8H
312e96a881 Force the expression mode for parameter fields within an extension (#5318) 2023-05-23 10:00:42 +02:00
D8H
c169c5037a Use instance default size as default custom size (#5334) 2023-05-23 09:41:43 +02:00
github-actions[bot]
a82ae304a0 Update translations [skip ci] (#5310)
Co-authored-by: AlexandreSi <AlexandreSi@users.noreply.github.com>
2023-05-23 09:17:53 +02:00
AlexandreS
adb30c5105 Fix default game slug on gd.games not long enough creating confusing errors (#5333) 2023-05-22 17:53:39 +02:00
Clément Pasteau
aad11f2518 Fix object group duplication crashing the app (#5331) 2023-05-22 16:11:15 +02:00
AlexandreS
737d389493 Prevent creating a global group with the same name as a default group and vice versa (#5332) 2023-05-22 16:05:13 +02:00
Clément Pasteau
4b071fbf17 Fix pixi js version (#5330)
Do not show in changelog
2023-05-22 15:48:10 +02:00
AlexandreS
982a0c6e53 Fix Z position tween not working on 3D model objects
Also fixes a missing icon for 3D model resource in the resources tab of the project
2023-05-22 09:55:36 +02:00
AlexandreS
91adaac722 Fix random color used in transparent sprites in 2D+3D rendering (#5314) 2023-05-19 17:39:17 +02:00
Clément Pasteau
138a7c9abe Fix format (#5313)
Don't show in changelog
2023-05-19 14:48:03 +02:00
AlexandreS
c7099d694e Improve build card name editing UX (#5312) 2023-05-19 14:47:38 +02:00
D8H
fde1039707 Fix the isCameraRotatedIn3D condition. (#5311)
Do not show in changelog
2023-05-19 12:13:40 +02:00
D8H
73e087936a Fix mouse and multitouch controls when 3D cameras are rotated (#5309) 2023-05-19 11:37:55 +02:00
AlexandreS
260fd5949e Upgrade posthog-js [skip ci] (#5308)
Only show in developer changelog
2023-05-19 11:29:17 +02:00
github-actions[bot]
a4939181a5 Update translations [skip ci] (#5307)
Co-authored-by: AlexandreSi <AlexandreSi@users.noreply.github.com>
2023-05-19 11:12:15 +02:00
AlexandreS
cab56451ff Bump newIDE version (#5306) 2023-05-19 10:12:22 +02:00
github-actions[bot]
7b32bbacd6 Update translations [skip ci] (#5300) 2023-05-19 10:02:01 +02:00
AlexandreS
a15707d655 Use correct reference to layer (#5305)
[skip-ci]
2023-05-19 10:00:58 +02:00
Aurélien Vivet
714a9f1198 Add a condition to check if a scene exists (#5297) 2023-05-19 09:24:15 +02:00
AlexandreS
2a91c54b0e Remove forgotten ref (#5304)
Don't show in changelog
2023-05-18 17:33:31 +02:00
Florian Rival
3272b8eda7 Fix 2D rendering sometimes occluding/cutting semi transparent 3D objects 2023-05-18 16:58:42 +02:00
AlexandreS
cd432a22b6 Fix crash when working with instances in the scene editor (#5303) 2023-05-18 16:28:19 +02:00
AlexandreS
9ac483156e Unsubscribe effects and asynchronous tasks (#5301)
Only show in developer changelog
2023-05-17 17:44:18 +02:00
AlexandreS
d0f39027af Slightly lighten project file when lots of comments are present in the events sheets (#5299) 2023-05-17 16:45:19 +02:00
348 changed files with 15514 additions and 4047 deletions

View File

@@ -17,31 +17,23 @@ jobs:
build-storybook:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 50
- uses: actions/setup-node@v3
with:
node-version: 16
cache: 'npm'
cache-dependency-path: 'newIDE/app/package-lock.json'
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v1
uses: aws-actions/configure-aws-credentials@v2
with:
aws-access-key-id: ${{ secrets.BUILD_STORYBOOK_AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.BUILD_STORYBOOK_AWS_SECRET_ACCESS_KEY }}
aws-region: us-east-1
- uses: actions/checkout@v2
with:
fetch-depth: 50
# Cache npm dependencies to speed up the workflow
- name: Cache node modules
uses: actions/cache@v2
env:
cache-name: cache-newIDE-app-node_modules
with:
# npm cache files are stored in `~/.npm` on Linux/macOS
path: ~/.npm
key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('newIDE/app/package-lock.json') }}
- name: Install newIDE dependencies
run: npm install
working-directory: newIDE/app

View File

@@ -11,20 +11,12 @@ jobs:
extract-translations:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16
- uses: actions/checkout@v2
# Cache npm dependencies to speed up the workflow
- name: Cache node modules
uses: actions/cache@v2
env:
cache-name: cache-newIDE-app-node_modules
with:
# npm cache files are stored in `~/.npm` on Linux/macOS
path: ~/.npm
key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('newIDE/app/package-lock.json') }}
cache: 'npm'
cache-dependency-path: 'newIDE/app/package-lock.json'
- name: Install gettext
run: sudo apt update && sudo apt install gettext -y

View File

@@ -3,41 +3,17 @@ on:
issues:
types: [opened]
jobs:
autoclose:
runs-on: ubuntu-latest
steps:
- name: Autoclose issues about adding a bug without changing the bug report template
uses: arkon/issue-closer-action@v1.1
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
type: "body"
regex: ".*Scroll down to '\\.\\.\\.\\.'.*"
message: "Hi @${issue.user.login}! 👋 This issue was automatically closed because it seems that you have not included any steps to reproduce the bug.\n\nGitHub is a place for the technical development of GDevelop itself - you may want to go on the [forum](https://forum.gdevelop.io/), the Discord chat or [read the documentation](https://wiki.gdevelop.io/gdevelop5/start) to learn more about GDevelop. Thanks!"
- name: Autoclose known beta 105 web-app update bug
uses: arkon/issue-closer-action@v1.1
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
type: "body"
regex: ".*_instance.getRawFloatProperty is not a function.*"
message: "Hi @${issue.user.login}! 👋 This issue was automatically closed as this seems to be a known bug. It can be solved by **closing entirely the web-app and opening it again**. This will allow the web-app to auto-update and the problem should be gone."
- name: Autoclose known beta 114 web-app update bug
uses: arkon/issue-closer-action@v1.1
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
type: "body"
regex: ".*getAssociatedSettings is not a function.*"
message: "Hi @${issue.user.login}! 👋 This issue was automatically closed as this seems to be a known bug. It can be solved by **closing entirely the web-app and opening it again**. This will allow the web-app to auto-update and the problem should be gone."
autocomment:
runs-on: ubuntu-latest
if: contains(github.event.issue.body, 'The node to be removed is not a child of this node')
steps:
- name: Autocomment indications on bug if it looks like #3453
uses: peter-evans/create-or-update-comment@v1
with:
issue-number: ${{ github.event.issue.number }}
body: |
Hi @${{ github.actor }}!
Thank you for taking the time to open an issue.
- name: Autocomment indications on bug if it looks like #3453
uses: peter-evans/create-or-update-comment@v3
with:
token: ${{ secrets.GITHUB_TOKEN }}
issue-number: ${{ github.event.issue.number }}
body: |
Hi @${{ github.actor }}!
Thank you for taking the time to open an issue.
The solved issue #3453 mentioned a similar error, maybe it could help fix this new issue.
token: ${{ secrets.GITHUB_TOKEN }}
The solved issue #3453 mentioned a similar error, maybe it could help fix this new issue.

View File

@@ -1,29 +1,53 @@
name: GDevelop Issues automatic workflow
on:
pull_request:
types: [opened]
types:
- opened
- reopened
- synchronize
jobs:
read-locales-metadata:
if: contains(github.event.pull_request.title, '[Auto PR] Update translations')
if: github.event.pull_request.title == '[Auto PR] Update translations'
runs-on: ubuntu-latest
env:
COMMENT_TITLE: "Translation ratio changes"
steps:
- name: Read and format locales metadata
run: |
LANS=($(git diff HEAD^ HEAD --unified=5 newIDE/app/src/locales/LocalesMetadata.js | tail +6 | grep -E "^\s+\"languageName" | sed -E "s/^ *\"languageName\": \"//g" | sed -E "s/\",//g" | sed -E "s/ /_/g"))
ADDS=($(git diff HEAD^ HEAD --unified=0 newIDE/app/src/locales/LocalesMetadata.js | tail +6 | grep -E "^\+\s*\"translationRatio\"" | sed -E "s/^\+ *\"translationRatio\": //g"))
SUBS=($(git diff HEAD^ HEAD --unified=0 newIDE/app/src/locales/LocalesMetadata.js | tail +6 | grep -E "^\-\s*\"translationRatio\"" | sed -E "s/^\- *\"translationRatio\": //g"))
touch sumup.txt
for index in ${!ADDS[@]}; do
echo ${LANS[index]} | sed -E "s/_/ /g" >> sumup.txt
DELTA=$(bc <<< "scale=2;(${ADDS[index]}-${SUBS[index]})*100/1")
echo $DELTA % >> sumup.txt
done
- name: Store sumup in outputs
id: sumup
run: echo "::set-output name=sumupFileContent::$(cat sumup.txt)"
- name: Autocomment pull request with sumup
uses: peter-evans/create-or-update-comment@v1
with:
issue-number: ${{ github.event.number }}
body: ${{ steps.sumup.outputs.sumupFileContent}}
token: ${{ secrets.GITHUB_TOKEN }}
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Read and format locales metadata
env:
BASE: ${{ github.event.pull_request.base.sha }}
HEAD: ${{ github.event.pull_request.head.sha }}
run: |
LANS=($(git diff $BASE...$HEAD -- newIDE/app/src/locales/LocalesMetadata.js | grep -E "^\s+\"languageName" | sed -E "s/^ *\"languageName\": \"//g" | sed -E "s/\",//g" | sed -E "s/ /_/g"))
ADDS=($(git diff $BASE...$HEAD -- newIDE/app/src/locales/LocalesMetadata.js | grep -E "^\+\s*\"translationRatio\"" | sed -E "s/^\+ *\"translationRatio\": //g"))
SUBS=($(git diff $BASE...$HEAD -- newIDE/app/src/locales/LocalesMetadata.js | grep -E "^\-\s*\"translationRatio\"" | sed -E "s/^\- *\"translationRatio\": //g"))
touch sumup.md
echo "## $COMMENT_TITLE" >> sumup.md
echo "" >> sumup.md
echo "| Language | Change |" >> sumup.md
echo "| --- | --- |" >> sumup.md
for index in ${!ADDS[@]}; do
DELTA=$(echo "scale=3; (${ADDS[index]} - ${SUBS[index]})*100/1" | bc)
if (( $(echo "$DELTA == 0" | bc -l) )); then
continue
fi
LANGUAGE=${LANS[index]//_/ }
echo "| $LANGUAGE | $(printf "%1.3f" $DELTA) % |" >> sumup.md
done
- name: Find Comment
uses: peter-evans/find-comment@v2
id: fc
with:
token: ${{ secrets.GITHUB_TOKEN }}
issue-number: ${{ github.event.number }}
body-includes: ${{ env.COMMENT_TITLE }}
- name: Autocomment pull request with sumup
uses: peter-evans/create-or-update-comment@v3
with:
token: ${{ secrets.GITHUB_TOKEN }}
issue-number: ${{ github.event.number }}
comment-id: ${{ steps.fc.outputs.comment-id }}
edit-mode: replace
body-path: "sumup.md"

View File

@@ -14,20 +14,12 @@ jobs:
update-translations:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16
- uses: actions/checkout@v2
# Cache npm dependencies to speed up the workflow
- name: Cache node modules
uses: actions/cache@v2
env:
cache-name: cache-newIDE-app-node_modules
with:
# npm cache files are stored in `~/.npm` on Linux/macOS
path: ~/.npm
key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('newIDE/app/package-lock.json') }}
cache: 'npm'
cache-dependency-path: 'newIDE/app/package-lock.json'
- name: Install gettext
run: sudo apt update && sudo apt install gettext -y
@@ -64,7 +56,7 @@ jobs:
working-directory: newIDE/app
- name: Create a Pull Request with the changes
uses: peter-evans/create-pull-request@v3.10.1
uses: peter-evans/create-pull-request@v5
with:
commit-message: Update translations [skip ci]
branch: chore/update-translations

View File

@@ -127,5 +127,7 @@
"flow.useNPMPackagedFlow": true,
// Clang format styling (duplicated in scripts/CMakeClangUtils.txt)
"C_Cpp.clang_format_style": "{BasedOnStyle: Google, BinPackParameters: false, BinPackArguments: false}"
"C_Cpp.clang_format_style": "{BasedOnStyle: Google, BinPackParameters: false, BinPackArguments: false}",
"prettier.prettierPath": "./GDJS/node_modules/prettier",
"prettier.configPath": "./GDJS/.prettierrc"
}

View File

@@ -38,7 +38,7 @@ void CommentEvent::SerializeTo(SerializerElement &element) const {
.SetAttribute("textB", textB);
element.AddChild("comment").SetValue(com1);
element.AddChild("comment2").SetValue(com2);
if (!com2.empty()) element.AddChild("comment2").SetValue(com2);
}
void CommentEvent::UnserializeFrom(gd::Project &project,
@@ -53,7 +53,9 @@ void CommentEvent::UnserializeFrom(gd::Project &project,
textB = colorElement.GetIntAttribute("textB");
com1 = element.GetChild("comment", 0, "Com1").GetValue().GetString();
com2 = element.GetChild("comment2", 0, "Com2").GetValue().GetString();
if (element.HasChild("comment2")) {
com2 = element.GetChild("comment2", 0, "Com2").GetValue().GetString();
}
}
} // namespace gd

View File

@@ -1019,15 +1019,15 @@ gd::String EventsCodeGenerator::GenerateFreeCondition(
bool conditionInverted,
gd::EventsCodeGenerationContext& context) {
// Generate call
gd::String predicat;
gd::String predicate;
if (instrInfos.codeExtraInformation.type == "number" ||
instrInfos.codeExtraInformation.type == "string") {
predicat = GenerateRelationalOperatorCall(
predicate = GenerateRelationalOperatorCall(
instrInfos,
arguments,
instrInfos.codeExtraInformation.functionCallName);
} else {
predicat = instrInfos.codeExtraInformation.functionCallName + "(" +
predicate = instrInfos.codeExtraInformation.functionCallName + "(" +
GenerateArgumentsList(arguments, 0) + ")";
}
@@ -1040,10 +1040,10 @@ gd::String EventsCodeGenerator::GenerateFreeCondition(
conditionAlreadyTakeCareOfInversion = true;
}
if (!conditionAlreadyTakeCareOfInversion && conditionInverted)
predicat = GenerateNegatedPredicat(predicat);
predicate = GenerateNegatedPredicate(predicate);
// Generate condition code
return returnBoolean + " = " + predicat + ";\n";
return returnBoolean + " = " + predicate + ";\n";
}
gd::String EventsCodeGenerator::GenerateObjectCondition(
@@ -1065,18 +1065,18 @@ gd::String EventsCodeGenerator::GenerateObjectCondition(
instrInfos.codeExtraInformation.functionCallName;
// Create call
gd::String predicat;
gd::String predicate;
if ((instrInfos.codeExtraInformation.type == "number" ||
instrInfos.codeExtraInformation.type == "string")) {
predicat = GenerateRelationalOperatorCall(
predicate = GenerateRelationalOperatorCall(
instrInfos, arguments, objectFunctionCallNamePart, 1);
} else {
predicat = objectFunctionCallNamePart + "(" +
predicate = objectFunctionCallNamePart + "(" +
GenerateArgumentsList(arguments, 1) + ")";
}
if (conditionInverted) predicat = GenerateNegatedPredicat(predicat);
if (conditionInverted) predicate = GenerateNegatedPredicate(predicate);
return "For each picked object \"" + objectName + "\", check " + predicat +
return "For each picked object \"" + objectName + "\", check " + predicate +
".\n";
}
@@ -1090,16 +1090,16 @@ gd::String EventsCodeGenerator::GenerateBehaviorCondition(
bool conditionInverted,
gd::EventsCodeGenerationContext& context) {
// Create call
gd::String predicat;
gd::String predicate;
if ((instrInfos.codeExtraInformation.type == "number" ||
instrInfos.codeExtraInformation.type == "string")) {
predicat = GenerateRelationalOperatorCall(instrInfos, arguments, "", 2);
predicate = GenerateRelationalOperatorCall(instrInfos, arguments, "", 2);
} else {
predicat = "(" + GenerateArgumentsList(arguments, 2) + ")";
predicate = "(" + GenerateArgumentsList(arguments, 2) + ")";
}
if (conditionInverted) predicat = GenerateNegatedPredicat(predicat);
if (conditionInverted) predicate = GenerateNegatedPredicate(predicate);
return "For each picked object \"" + objectName + "\", check " + predicat +
return "For each picked object \"" + objectName + "\", check " + predicate +
" for behavior \"" + behaviorName + "\".\n";
}

View File

@@ -668,13 +668,13 @@ class GD_CORE_API EventsCodeGenerator {
};
/**
* \brief Must negate a predicat.
* \brief Must negate a predicate.
*
* The default implementation generates C-style code : It wraps the predicat
* The default implementation generates C-style code : It wraps the predicate
* inside parenthesis and add a !.
*/
virtual gd::String GenerateNegatedPredicat(const gd::String& predicat) const {
return "!(" + predicat + ")";
virtual gd::String GenerateNegatedPredicate(const gd::String& predicate) const {
return "!(" + predicate + ")";
};
virtual gd::String GenerateFreeCondition(

View File

@@ -66,6 +66,11 @@ class GD_CORE_API ReadOnlyEventVisitor {
* Note that VisitEvent is also called with this event.
*/
virtual void VisitLinkEvent(const gd::LinkEvent& linkEvent) = 0;
/**
* @brief Abort the iteration on the events.
*/
virtual void StopAnyEventIteration() = 0;
};
}

View File

@@ -5,6 +5,7 @@
*/
#include "EventsList.h"
#include "GDCore/Events/Event.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Tools/Log.h"
@@ -100,9 +101,8 @@ bool EventsList::Contains(const gd::BaseEvent& eventToSearch,
}
bool EventsList::MoveEventToAnotherEventsList(const gd::BaseEvent& eventToMove,
gd::EventsList& newEventsList,
std::size_t newPosition) {
gd::EventsList& newEventsList,
std::size_t newPosition) {
for (std::size_t i = 0; i < GetEventsCount(); ++i) {
if (events[i].get() == &eventToMove) {
std::shared_ptr<BaseEvent> event = events[i];

View File

@@ -69,12 +69,12 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsFileExtension(
extension
.AddAction(
"EcrireFichierExp",
_("Write a value"),
_("Write the result of the expression in the stored data, in the "
_("Save a value"),
_("Save the result of the expression in the stored data, in the "
"specified element.\nSpecify the structure leading to the "
"element using / (example : Root/Level/Current)\nSpaces are "
"forbidden in element names."),
_("Write _PARAM2_ in _PARAM1_ of storage _PARAM0_"),
_("Save _PARAM2_ in _PARAM1_ of storage _PARAM0_"),
"",
"res/actions/fichier24.png",
"res/actions/fichier.png")
@@ -85,12 +85,12 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsFileExtension(
extension
.AddAction(
"EcrireFichierTxt",
_("Write a text"),
_("Write the text in the specified storage, in the specified "
_("Save a text"),
_("Save the text in the specified storage, in the specified "
"element.\nSpecify "
"the structure leading to the element using / (example : "
"Root/Level/Current)\nSpaces are forbidden in element names."),
_("Write _PARAM2_ in _PARAM1_ of storage _PARAM0_"),
_("Save _PARAM2_ in _PARAM1_ of storage _PARAM0_"),
"",
"res/actions/fichier24.png",
"res/actions/fichier.png")
@@ -101,13 +101,13 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsFileExtension(
extension
.AddAction(
"LireFichierExp",
_("Read a value"),
_("Read the value saved in the specified element and store it in a "
_("Load a value"),
_("Load the value saved in the specified element and store it in a "
"scene "
"variable.\nSpecify the structure leading to the element using / "
"(example : Root/Level/Current)\nSpaces are forbidden in element "
"names."),
_("Read _PARAM1_ from storage _PARAM0_ and store value in _PARAM3_"),
_("Load _PARAM1_ from storage _PARAM0_ and store value in _PARAM3_"),
"",
"res/actions/fichier24.png",
"res/actions/fichier.png")
@@ -119,13 +119,13 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsFileExtension(
extension
.AddAction(
"LireFichierTxt",
_("Read a text"),
_("Read the text saved in the specified element and store it in a "
_("Load a text"),
_("Load the text saved in the specified element and store it in a "
"scene "
"variable.\nSpecify the structure leading to the element using / "
"(example : Root/Level/Current)\nSpaces are forbidden in element "
"names."),
_("Read _PARAM1_ from storage _PARAM0_ and store as text in "
_("Load _PARAM1_ from storage _PARAM0_ and store as text in "
"_PARAM3_"),
"",
"res/actions/fichier24.png",

View File

@@ -54,6 +54,19 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSceneExtension(
.AddCodeOnlyParameter("currentScene", "")
.MarkAsSimple();
extension
.AddCondition("DoesSceneExist",
_("Does scene exist"),
_("Check if scene exists."),
_("Scene _PARAM1_ exists"),
"",
"res/actions/texte.png",
"res/actions/texte.png")
.SetHelpPath("/interface/scene-editor/events")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("sceneName", _("Name of the scene to check"))
.MarkAsSimple();
extension
.AddAction("Scene",
_("Change the scene"),

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

@@ -135,11 +135,11 @@ MetadataProvider::GetExtensionAndConditionMetadata(const gd::Platform& platform,
const auto& objects = extension->GetExtensionObjectsTypes();
for (const gd::String& extObjectType : objects) {
const auto& allObjetsConditions =
const auto& allObjectsConditions =
extension->GetAllConditionsForObject(extObjectType);
if (allObjetsConditions.find(conditionType) != allObjetsConditions.end())
if (allObjectsConditions.find(conditionType) != allObjectsConditions.end())
return ExtensionAndMetadata<InstructionMetadata>(
*extension, allObjetsConditions.find(conditionType)->second);
*extension, allObjectsConditions.find(conditionType)->second);
}
const auto& autos = extension->GetBehaviorsTypes();

View File

@@ -84,6 +84,9 @@ void ReadOnlyArbitraryEventsWorker::VisitEventList(const gd::EventsList& events)
DoVisitEventList(events);
for (std::size_t i = 0; i < events.size(); ++i) {
if (shouldStopIteration) {
break;
}
events[i].AcceptVisitor(*this);
if (events[i].CanHaveSubEvents()) {
@@ -98,11 +101,17 @@ void ReadOnlyArbitraryEventsWorker::VisitEvent(const gd::BaseEvent& event) {
const vector<const gd::InstructionsList*> conditionsVectors =
event.GetAllConditionsVectors();
for (std::size_t j = 0; j < conditionsVectors.size(); ++j) {
if (shouldStopIteration) {
break;
}
VisitInstructionList(*conditionsVectors[j], true);
}
const vector<const gd::InstructionsList*> actionsVectors = event.GetAllActionsVectors();
for (std::size_t j = 0; j < actionsVectors.size(); ++j) {
if (shouldStopIteration) {
break;
}
VisitInstructionList(*actionsVectors[j], false);
}
}
@@ -116,6 +125,9 @@ void ReadOnlyArbitraryEventsWorker::VisitInstructionList(
DoVisitInstructionList(instructions, areConditions);
for (std::size_t i = 0; i < instructions.size(); ++i) {
if (shouldStopIteration) {
break;
}
VisitInstruction(instructions[i], areConditions);
if (!instructions[i].GetSubInstructions().empty()) {
VisitInstructionList(instructions[i].GetSubInstructions(),
@@ -129,6 +141,10 @@ void ReadOnlyArbitraryEventsWorker::VisitInstruction(const gd::Instruction& inst
DoVisitInstruction(instruction, isCondition);
}
void ReadOnlyArbitraryEventsWorker::StopAnyEventIteration() {
shouldStopIteration = true;
}
ReadOnlyArbitraryEventsWorkerWithContext::~ReadOnlyArbitraryEventsWorkerWithContext() {}
} // namespace gd

View File

@@ -144,7 +144,7 @@ class GD_CORE_API ArbitraryEventsWorkerWithContext
*/
class GD_CORE_API ReadOnlyArbitraryEventsWorker : private ReadOnlyEventVisitor {
public:
ReadOnlyArbitraryEventsWorker(){};
ReadOnlyArbitraryEventsWorker() : shouldStopIteration(false) {};
virtual ~ReadOnlyArbitraryEventsWorker();
/**
@@ -152,6 +152,9 @@ class GD_CORE_API ReadOnlyArbitraryEventsWorker : private ReadOnlyEventVisitor {
*/
void Launch(const gd::EventsList& events) { VisitEventList(events); };
protected:
void StopAnyEventIteration() override;
private:
void VisitEventList(const gd::EventsList& events);
void VisitEvent(const gd::BaseEvent& event) override;
@@ -188,6 +191,8 @@ class GD_CORE_API ReadOnlyArbitraryEventsWorker : private ReadOnlyEventVisitor {
*/
virtual void DoVisitInstruction(const gd::Instruction& instruction,
bool isCondition) {};
bool shouldStopIteration;
};
/**

View File

@@ -0,0 +1,175 @@
/*
* GDevelop Core
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "EventsFunctionSelfCallChecker.h"
#include "GDCore/Events/Expression.h"
#include "GDCore/Events/Parsers/ExpressionParser2Node.h"
#include "GDCore/Events/Parsers/ExpressionParser2NodeWorker.h"
#include "GDCore/Extensions/Metadata/ExpressionMetadata.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Extensions/Metadata/ParameterMetadataTools.h"
#include "GDCore/Extensions/PlatformExtension.h"
#include "GDCore/IDE/Events/ArbitraryEventsWorker.h"
#include "GDCore/Project/EventsBasedBehavior.h"
#include "GDCore/Project/EventsBasedObject.h"
#include "GDCore/Project/EventsFunction.h"
#include "GDCore/Project/EventsFunctionsExtension.h"
#include "GDCore/Project/Object.h"
#include "GDCore/Project/Project.h"
#include "GDCore/String.h"
#include "GDCore/Tools/Log.h"
namespace gd {
/**
* \brief Check the type of the first condition or action.
*/
class GD_CORE_API FirstInstructionTypeChecker
: public ReadOnlyArbitraryEventsWorker {
public:
FirstInstructionTypeChecker(const gd::String &eventFunctionType_,
const bool isEventFunctionCondition_)
: eventFunctionType(eventFunctionType_),
isEventFunctionCondition(isEventFunctionCondition_),
isOnlyCallingItself(false){};
virtual ~FirstInstructionTypeChecker(){};
void DoVisitInstruction(const gd::Instruction &instruction,
bool isCondition) override {
if (isCondition == isEventFunctionCondition) {
isOnlyCallingItself = instruction.GetType() == eventFunctionType;
StopAnyEventIteration();
}
};
bool isOnlyCallingItself;
private:
gd::String eventFunctionType;
bool isEventFunctionCondition;
};
/**
* \brief Check the type of the first condition or action.
*/
class GD_CORE_API FirstActionExpressionTypeChecker
: public ReadOnlyArbitraryEventsWorker,
ExpressionParser2NodeWorker {
public:
FirstActionExpressionTypeChecker(const gd::Platform &platform_,
const gd::String &eventFunctionType_)
: platform(platform_), eventFunctionType(eventFunctionType_),
isOnlyCallingItself(false){};
virtual ~FirstActionExpressionTypeChecker(){};
void DoVisitInstruction(const gd::Instruction &instruction,
bool isCondition) override {
// Typically, it should be a "return" action.
if (!isCondition) {
gd::String lastObjectParameter = "";
const gd::InstructionMetadata &instrInfos =
MetadataProvider::GetActionMetadata(platform, instruction.GetType());
for (std::size_t pNb = 0; pNb < instrInfos.parameters.size(); ++pNb) {
if (ParameterMetadata::IsExpression(
"number", instrInfos.parameters[pNb].GetType()) ||
ParameterMetadata::IsExpression(
"string", instrInfos.parameters[pNb].GetType())) {
auto node = instruction.GetParameter(pNb).GetRootNode();
node->Visit(*this);
}
}
StopAnyEventIteration();
}
}
// Only check expressions that directly calls a function.
void OnVisitFunctionCallNode(FunctionCallNode &node) override {
isOnlyCallingItself |= node.functionName == eventFunctionType;
}
// Handle extra parenthesis.
void OnVisitSubExpressionNode(SubExpressionNode &node) override {
node.expression->Visit(*this);
}
void OnVisitOperatorNode(OperatorNode &node) override {}
// Handle sign that could have been forgotten
void OnVisitUnaryOperatorNode(UnaryOperatorNode &node) override {
node.factor->Visit(*this);
}
void OnVisitNumberNode(NumberNode &node) override {}
void OnVisitTextNode(TextNode &node) override {}
void OnVisitVariableNode(VariableNode &node) override {}
void OnVisitVariableAccessorNode(VariableAccessorNode &node) override {}
void OnVisitVariableBracketAccessorNode(
VariableBracketAccessorNode &node) override {}
void OnVisitIdentifierNode(IdentifierNode &node) override {}
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode &node) override {}
void OnVisitEmptyNode(EmptyNode &node) override {}
bool isOnlyCallingItself;
private:
const gd::Platform &platform;
gd::String eventFunctionType;
};
bool EventsFunctionSelfCallChecker::IsOnlyCallingItself(
const gd::Project &project, const gd::String &functionFullType,
const gd::EventsFunction &eventsFunction) {
bool isOnlyCallingItself = false;
if (eventsFunction.IsExpression()) {
FirstActionExpressionTypeChecker eventWorker(project.GetCurrentPlatform(),
functionFullType);
eventWorker.Launch(eventsFunction.GetEvents());
isOnlyCallingItself = eventWorker.isOnlyCallingItself;
} else {
FirstInstructionTypeChecker eventWorker(functionFullType,
!eventsFunction.IsAction());
eventWorker.Launch(eventsFunction.GetEvents());
isOnlyCallingItself = eventWorker.isOnlyCallingItself;
}
return isOnlyCallingItself;
}
bool EventsFunctionSelfCallChecker::IsFreeFunctionOnlyCallingItself(
const gd::Project &project,
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
const gd::EventsFunction &eventsFunction) {
return IsOnlyCallingItself(
project,
PlatformExtension::GetEventsFunctionFullType(
eventsFunctionsExtension.GetName(), eventsFunction.GetName()),
eventsFunction);
}
bool EventsFunctionSelfCallChecker::IsBehaviorFunctionOnlyCallingItself(
const gd::Project &project,
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
const gd::EventsBasedBehavior &eventsBasedBehavior,
const gd::EventsFunction &eventsFunction) {
return IsOnlyCallingItself(
project,
PlatformExtension::GetBehaviorEventsFunctionFullType(
eventsFunctionsExtension.GetName(), eventsBasedBehavior.GetName(),
eventsFunction.GetName()),
eventsFunction);
}
bool EventsFunctionSelfCallChecker::IsObjectFunctionOnlyCallingItself(
const gd::Project &project,
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
const gd::EventsBasedObject &eventsBasedObject,
const gd::EventsFunction &eventsFunction) {
return IsOnlyCallingItself(project,
PlatformExtension::GetObjectEventsFunctionFullType(
eventsFunctionsExtension.GetName(),
eventsBasedObject.GetName(),
eventsFunction.GetName()),
eventsFunction);
}
} // namespace gd

View File

@@ -0,0 +1,64 @@
/*
* GDevelop Core
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#pragma once
#include "GDCore/String.h"
#include <vector>
namespace gd {
class Project;
class EventsFunctionsExtension;
class EventsFunctionsContainer;
class EventsFunction;
class EventsBasedBehavior;
class EventsBasedObject;
} // namespace gd
namespace gd {
/**
* \brief Check if functions are only calling themselves.
*
* It allows to detect mistakes when implementing functions for compatibility
* after a function renaming.
* Infinite loops can happens when the legacy function call itself instead of
* the new function.
*
* \note Only the first instruction or the expressions of the first action is
* checked.
*/
class GD_CORE_API EventsFunctionSelfCallChecker {
public:
/**
* \brief Check if a free function is only calling itself.
*/
static bool IsFreeFunctionOnlyCallingItself(
const gd::Project &project,
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
const gd::EventsFunction &eventsFunction);
/**
* \brief Check if a behavior function is only calling itself.
*/
static bool IsBehaviorFunctionOnlyCallingItself(
const gd::Project &project,
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
const gd::EventsBasedBehavior &eventsBasedBehavior,
const gd::EventsFunction &eventsFunction);
/**
* \brief Check if an object function is only calling itself.
*/
static bool IsObjectFunctionOnlyCallingItself(
const gd::Project &project,
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
const gd::EventsBasedObject &eventsBasedObject,
const gd::EventsFunction &eventsFunction);
private:
static bool IsOnlyCallingItself(const gd::Project &project,
const gd::String &functionFullType,
const gd::EventsFunction &eventsFunction);
};
} // namespace gd

View File

@@ -642,7 +642,7 @@ std::vector<EventsSearchResult> EventsRefactorer::ReplaceStringInEvents(
return modifiedEvents;
}
gd::String ReplaceAllOccurencesCaseUnsensitive(gd::String context,
gd::String ReplaceAllOccurrencesCaseInsensitive(gd::String context,
gd::String from,
const gd::String& to) {
size_t lookHere = 0;
@@ -673,7 +673,7 @@ bool EventsRefactorer::ReplaceStringInActions(gd::ObjectsContainer& project,
matchCase
? actions[aId].GetParameter(pNb).GetPlainString().FindAndReplace(
toReplace, newString, true)
: ReplaceAllOccurencesCaseUnsensitive(
: ReplaceAllOccurrencesCaseInsensitive(
actions[aId].GetParameter(pNb).GetPlainString(),
toReplace,
newString);
@@ -713,7 +713,7 @@ bool EventsRefactorer::ReplaceStringInConditions(
.GetParameter(pNb)
.GetPlainString()
.FindAndReplace(toReplace, newString, true)
: ReplaceAllOccurencesCaseUnsensitive(
: ReplaceAllOccurrencesCaseInsensitive(
conditions[cId].GetParameter(pNb).GetPlainString(),
toReplace,
newString);
@@ -749,7 +749,7 @@ bool EventsRefactorer::ReplaceStringInEventSearchableStrings(
for (std::size_t sNb = 0; sNb < stringEvent.size(); ++sNb) {
gd::String newStringEvent =
matchCase ? stringEvent[sNb].FindAndReplace(toReplace, newString, true)
: ReplaceAllOccurencesCaseUnsensitive(
: ReplaceAllOccurrencesCaseInsensitive(
stringEvent[sNb], toReplace, newString);
newEventStrings.push_back(newStringEvent);
}

View File

@@ -16,7 +16,7 @@ class EventsBasedObject;
class AbstractEventsBasedEntity;
class PropertyDescriptor;
class NamedPropertyDescriptor;
} // namespace gd
} // namespace gd
namespace gd {
@@ -24,22 +24,24 @@ namespace gd {
* \brief Generate a getter and a setter functions for properties.
*/
class GD_CORE_API PropertyFunctionGenerator {
public:
public:
/**
* \brief Generate a getter and a setter for the given behavior property.
*/
static void GenerateBehaviorGetterAndSetter(
gd::Project &project, gd::EventsFunctionsExtension &extension,
gd::Project &project,
gd::EventsFunctionsExtension &extension,
gd::EventsBasedBehavior &eventsBasedBehavior,
const gd::NamedPropertyDescriptor &property, bool isSharedProperties);
const gd::NamedPropertyDescriptor &property,
bool isSharedProperties);
/**
* \brief Generate a getter and a setter for the given object property.
*/
static void
GenerateObjectGetterAndSetter(gd::Project &project,
gd::EventsFunctionsExtension &extension,
gd::EventsBasedObject &eventsBasedObject,
const gd::NamedPropertyDescriptor &property);
static void GenerateObjectGetterAndSetter(
gd::Project &project,
gd::EventsFunctionsExtension &extension,
gd::EventsBasedObject &eventsBasedObject,
const gd::NamedPropertyDescriptor &property);
static bool CanGenerateGetterAndSetter(
const gd::AbstractEventsBasedEntity &eventsBasedEntity,
const gd::NamedPropertyDescriptor &property);
@@ -50,23 +52,26 @@ public:
static void GenerateConditionSkeleton(gd::Project &project,
gd::EventsFunction &eventFunction);
~PropertyFunctionGenerator();
~PropertyFunctionGenerator(){};
private:
private:
static void GenerateGetterAndSetter(
gd::Project &project, gd::EventsFunctionsExtension &extension,
gd::Project &project,
gd::EventsFunctionsExtension &extension,
gd::AbstractEventsBasedEntity &eventsBasedEntity,
const gd::NamedPropertyDescriptor &property, const gd::String &objectType,
bool isBehavior, bool isSharedProperties);
const gd::NamedPropertyDescriptor &property,
const gd::String &objectType,
bool isBehavior,
bool isSharedProperties);
static gd::String CapitalizeFirstLetter(const gd::String &string);
static gd::String UnCapitalizeFirstLetter(const gd::String &string);
static gd::String
GetStringifiedExtraInfo(const gd::PropertyDescriptor &property);
static gd::String GetStringifiedExtraInfo(
const gd::PropertyDescriptor &property);
PropertyFunctionGenerator();
};
} // namespace gd
} // namespace gd
#endif // GDCORE_PROPERTYFUNCTIONGENERATOR_H
#endif // GDCORE_PROPERTYFUNCTIONGENERATOR_H

View File

@@ -153,6 +153,8 @@ void CustomObjectConfiguration::ExposeResources(gd::ArbitraryResourceWorker& wor
worker.ExposeTileset(newPropertyValue);
} else if (resourceType == "bitmapFont") {
worker.ExposeBitmapFont(newPropertyValue);
} else if (resourceType == "model3D") {
worker.ExposeModel3D(newPropertyValue);
}
if (newPropertyValue != oldPropertyValue) {

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

@@ -68,14 +68,14 @@ gd::InitialInstance& InitialInstancesContainer::InsertNewInitialInstance() {
}
void InitialInstancesContainer::RemoveInstanceIf(
std::function<bool(const gd::InitialInstance&)> predicat) {
std::function<bool(const gd::InitialInstance&)> predicate) {
// Note that we can't use eraseremove idiom here because remove_if would
// move the instances, and the container must guarantee that
// iterators/pointers to instances always remain valid.
for (std::list<gd::InitialInstance>::iterator it = initialInstances.begin(),
end = initialInstances.end();
it != end;) {
if (predicat(*it))
if (predicate(*it))
it = initialInstances.erase(it);
else
++it;

View File

@@ -178,7 +178,7 @@ class GD_CORE_API InitialInstancesContainer {
private:
void RemoveInstanceIf(
std::function<bool(const gd::InitialInstance &)> predicat);
std::function<bool(const gd::InitialInstance &)> predicate);
std::list<gd::InitialInstance> initialInstances;

View File

@@ -21,7 +21,10 @@ Layer::Layer()
followBaseLayerCamera(false),
camera3DNearPlaneDistance(0.1),
camera3DFarPlaneDistance(10000),
camera3DFieldOfView(45) {}
camera3DFieldOfView(45),
ambientLightColorR(200),
ambientLightColorG(200),
ambientLightColorB(200) {}
/**
* Change cameras count, automatically adding/removing them.

View File

@@ -5,17 +5,17 @@
*/
#ifndef GDCORE_OBJECT_H
#define GDCORE_OBJECT_H
#include "GDCore/Vector2.h"
#include <map>
#include <memory>
#include <vector>
#include "GDCore/Project/Behavior.h"
#include "GDCore/Project/ObjectConfiguration.h"
#include "GDCore/Project/EffectsContainer.h"
#include "GDCore/Project/ObjectConfiguration.h"
#include "GDCore/Project/VariablesContainer.h"
#include "GDCore/String.h"
#include "GDCore/Tools/MakeUnique.h"
#include "GDCore/Vector2.h"
namespace gd {
class PropertyDescriptor;
@@ -45,7 +45,7 @@ class GD_CORE_API Object {
/**
* Create a new object with the name passed as argument.
*
*
* Object takes the ownership of the configuration.
*/
Object(const gd::String& name,
@@ -104,7 +104,9 @@ class GD_CORE_API Object {
/** \brief Change the asset store id of the object.
*/
void SetAssetStoreId(const gd::String& assetStoreId_) { assetStoreId = assetStoreId_; };
void SetAssetStoreId(const gd::String& assetStoreId_) {
assetStoreId = assetStoreId_;
};
/** \brief Return the asset store id of the object.
*/
@@ -112,15 +114,11 @@ class GD_CORE_API Object {
/** \brief Change the type of the object.
*/
void SetType(const gd::String& type_) {
configuration->SetType(type_);
}
void SetType(const gd::String& type_) { configuration->SetType(type_); }
/** \brief Return the type of the object.
*/
const gd::String& GetType() const {
return configuration->GetType();
}
const gd::String& GetType() const { return configuration->GetType(); }
/** \brief Change the tags of the object.
*/
@@ -129,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
@@ -224,12 +226,12 @@ class GD_CORE_API Object {
* effects.
*/
gd::EffectsContainer& GetEffects() { return effectsContainer; }
///@}
///@}
/** \name Serialization
* Members functions related to serialization of the object
*/
///@{
/** \name Serialization
* Members functions related to serialization of the object
*/
///@{
/**
* \brief Serialize the object.
* \see DoSerializeTo
@@ -244,8 +246,9 @@ class GD_CORE_API Object {
///@}
protected:
gd::String name; ///< The full name of the object
gd::String assetStoreId; ///< The ID of the asset if the object comes from the store.
gd::String name; ///< The full name of the object
gd::String assetStoreId; ///< The ID of the asset if the object comes from
///< the store.
std::unique_ptr<gd::ObjectConfiguration> configuration;
std::map<gd::String, std::unique_ptr<gd::Behavior>>
behaviors; ///< Contains all behaviors and their properties for the
@@ -260,7 +263,7 @@ class GD_CORE_API Object {
/**
* Initialize object using another object. Used by copy-ctor and assign-op.
* Don't forget to update me if members were changed!
*
*
* It's needed because there is no default copy for a map of unique_ptr like
* behaviors and it must be a deep copy.
*/
@@ -283,14 +286,4 @@ struct ObjectHasName : public std::binary_function<std::unique_ptr<gd::Object>,
} // namespace gd
/**
* An object list is a vector containing (smart) pointers to objects.
*/
using ObjList = std::vector<std::unique_ptr<gd::Object>>;
/**
* Objects are usually managed thanks to (smart) pointers.
*/
using ObjSPtr = std::unique_ptr<gd::Object>;
#endif // GDCORE_OBJECT_H

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

@@ -5,8 +5,10 @@
*/
#include "ObjectGroup.h"
#include <algorithm>
#include <vector>
#include "GDCore/Serialization/SerializerElement.h"
#include "GDCore/String.h"
@@ -41,8 +43,9 @@ void ObjectGroup::SerializeTo(SerializerElement& element) const {
SerializerElement& objectsElement = element.AddChild("objects");
objectsElement.ConsiderAsArrayOf("object");
for (auto& name : GetAllObjectsNames())
for (auto& name : GetAllObjectsNames()) {
objectsElement.AddChild("object").SetAttribute("name", name);
}
}
void ObjectGroup::UnserializeFrom(const SerializerElement& element) {

View File

@@ -8,7 +8,9 @@
#define GDCORE_OBJECTGROUP_H
#include <utility>
#include <vector>
#include "GDCore/String.h"
namespace gd {
class SerializerElement;
}

View File

@@ -4,79 +4,113 @@
* reserved. This project is released under the MIT License.
*/
#include "GDCore/Project/ObjectGroupsContainer.h"
#include "ObjectGroupsContainer.h"
#include <memory>
#include "GDCore/Project/ObjectGroup.h"
#include "GDCore/Serialization/SerializerElement.h"
#include "GDCore/String.h"
#include "GDCore/Tools/MakeUnique.h"
namespace gd {
gd::ObjectGroup ObjectGroupsContainer::badGroup;
ObjectGroup ObjectGroupsContainer::badGroup;
ObjectGroupsContainer::ObjectGroupsContainer() {}
ObjectGroupsContainer::ObjectGroupsContainer(
const ObjectGroupsContainer& other) {
Init(other);
}
ObjectGroupsContainer& ObjectGroupsContainer::operator=(
const ObjectGroupsContainer& other) {
if (this != &other) Init(other);
return *this;
}
void ObjectGroupsContainer::Init(const ObjectGroupsContainer& other) {
objectGroups.clear();
for (auto& it : other.objectGroups) {
objectGroups.push_back(gd::make_unique<gd::ObjectGroup>(*it));
}
}
void ObjectGroupsContainer::SerializeTo(SerializerElement& element) const {
element.ConsiderAsArrayOf("group");
for (std::size_t i = 0; i < objectGroups.size(); ++i) {
objectGroups[i]->SerializeTo(element.AddChild("group"));
}
}
void ObjectGroupsContainer::UnserializeFrom(const SerializerElement& element) {
objectGroups.clear();
element.ConsiderAsArrayOf("group", "Groupe");
for (std::size_t i = 0; i < element.GetChildrenCount(); ++i) {
const SerializerElement& groupElement = element.GetChild(i);
gd::ObjectGroup& objectGroup =
InsertNew(element.GetStringAttribute("name", "", "nom"), -1);
objectGroup.UnserializeFrom(groupElement);
}
}
bool ObjectGroupsContainer::Has(const gd::String& name) const {
auto i = std::find_if(
objectGroups.begin(),
objectGroups.end(),
[&name](const ObjectGroup& group) { return group.GetName() == name; });
auto i = std::find_if(objectGroups.begin(),
objectGroups.end(),
[&name](const std::unique_ptr<gd::ObjectGroup>& group) {
return group->GetName() == name;
});
return (i != objectGroups.end());
}
ObjectGroup& ObjectGroupsContainer::Get(std::size_t index) {
if (index < objectGroups.size()) return objectGroups[index];
if (index < objectGroups.size()) return *objectGroups[index];
return badGroup;
}
const ObjectGroup& ObjectGroupsContainer::Get(std::size_t index) const {
if (index < objectGroups.size()) return objectGroups[index];
if (index < objectGroups.size()) return *objectGroups[index];
return badGroup;
}
ObjectGroup& ObjectGroupsContainer::Get(const gd::String& name) {
auto i = std::find_if(
objectGroups.begin(),
objectGroups.end(),
[&name](const ObjectGroup& group) { return group.GetName() == name; });
if (i != objectGroups.end()) return *i;
auto i = std::find_if(objectGroups.begin(),
objectGroups.end(),
[&name](const std::unique_ptr<gd::ObjectGroup>& group) {
return group->GetName() == name;
});
if (i != objectGroups.end()) return **i;
return badGroup;
}
const ObjectGroup& ObjectGroupsContainer::Get(const gd::String& name) const {
auto i = std::find_if(
objectGroups.begin(),
objectGroups.end(),
[&name](const ObjectGroup& group) { return group.GetName() == name; });
if (i != objectGroups.end()) return *i;
auto i = std::find_if(objectGroups.begin(),
objectGroups.end(),
[&name](const std::unique_ptr<gd::ObjectGroup>& group) {
return group->GetName() == name;
});
if (i != objectGroups.end()) return **i;
return badGroup;
}
ObjectGroup& ObjectGroupsContainer::Insert(const gd::ObjectGroup& group,
std::size_t position) {
if (position < objectGroups.size()) {
objectGroups.insert(objectGroups.begin() + position, group);
return objectGroups[position];
} else {
objectGroups.push_back(group);
return objectGroups.back();
}
}
#if defined(GD_IDE_ONLY)
void ObjectGroupsContainer::Remove(const gd::String& name) {
objectGroups.erase(std::remove_if(objectGroups.begin(),
objectGroups.end(),
[&name](const ObjectGroup& group) {
return group.GetName() == name;
}),
objectGroups.end());
objectGroups.erase(
std::remove_if(objectGroups.begin(),
objectGroups.end(),
[&name](const std::unique_ptr<gd::ObjectGroup>& group) {
return group->GetName() == name;
}),
objectGroups.end());
}
std::size_t ObjectGroupsContainer::GetPosition(const gd::String& name) const {
for (std::size_t i = 0; i < objectGroups.size(); ++i) {
if (objectGroups[i].GetName() == name) return i;
if (objectGroups[i]->GetName() == name) return i;
}
return gd::String::npos;
@@ -84,21 +118,36 @@ std::size_t ObjectGroupsContainer::GetPosition(const gd::String& name) const {
ObjectGroup& ObjectGroupsContainer::InsertNew(const gd::String& name,
std::size_t position) {
ObjectGroup newGroup;
newGroup.SetName(name);
return Insert(newGroup, position);
gd::ObjectGroup& newlyInsertedGroup = *(*(objectGroups.insert(
position < objectGroups.size() ? objectGroups.begin() + position
: objectGroups.end(),
gd::make_unique<gd::ObjectGroup>())));
newlyInsertedGroup.SetName(name);
return newlyInsertedGroup;
}
ObjectGroup& ObjectGroupsContainer::Insert(const gd::ObjectGroup& group,
std::size_t position) {
gd::ObjectGroup& newlyInsertedGroup = *(*(objectGroups.insert(
position < objectGroups.size() ? objectGroups.begin() + position
: objectGroups.end(),
gd::make_unique<gd::ObjectGroup>(group))));
return newlyInsertedGroup;
}
bool ObjectGroupsContainer::Rename(const gd::String& oldName,
const gd::String& newName) {
if (Has(newName)) return false;
auto i = std::find_if(objectGroups.begin(),
objectGroups.end(),
[&oldName](const ObjectGroup& group) {
return group.GetName() == oldName;
});
if (i != objectGroups.end()) i->SetName(newName);
auto i =
std::find_if(objectGroups.begin(),
objectGroups.end(),
[&oldName](const std::unique_ptr<gd::ObjectGroup>& group) {
return group->GetName() == oldName;
});
if (i != objectGroups.end()) {
(*i)->SetName(newName);
}
return true;
}
@@ -107,30 +156,10 @@ void ObjectGroupsContainer::Move(std::size_t oldIndex, std::size_t newIndex) {
if (oldIndex >= objectGroups.size() || newIndex >= objectGroups.size())
return;
auto group = objectGroups[oldIndex];
std::unique_ptr<gd::ObjectGroup> objectGroup =
std::move(objectGroups[oldIndex]);
objectGroups.erase(objectGroups.begin() + oldIndex);
Insert(group, newIndex);
}
#endif
void ObjectGroupsContainer::SerializeTo(SerializerElement& element) const {
element.ConsiderAsArrayOf("group");
for (auto& group : objectGroups) {
SerializerElement& groupElement = element.AddChild("group");
group.SerializeTo(groupElement);
}
}
void ObjectGroupsContainer::UnserializeFrom(const SerializerElement& element) {
objectGroups.clear();
element.ConsiderAsArrayOf("group", "Groupe");
for (std::size_t i = 0; i < element.GetChildrenCount(); ++i) {
SerializerElement& groupElement = element.GetChild(i);
gd::ObjectGroup objectGroup;
objectGroup.UnserializeFrom(groupElement);
objectGroups.push_back(objectGroup);
}
objectGroups.insert(objectGroups.begin() + newIndex, std::move(objectGroup));
}
} // namespace gd

View File

@@ -6,8 +6,10 @@
#ifndef GDCORE_OBJECTGROUPSCONTAINER_H
#define GDCORE_OBJECTGROUPSCONTAINER_H
#include <vector>
#include <algorithm>
#include <memory>
#include <vector>
#include "GDCore/Project/ObjectGroup.h"
#include "GDCore/String.h"
namespace gd {
@@ -26,8 +28,10 @@ namespace gd {
*/
class GD_CORE_API ObjectGroupsContainer {
public:
ObjectGroupsContainer(){};
ObjectGroupsContainer();
ObjectGroupsContainer(const ObjectGroupsContainer&);
virtual ~ObjectGroupsContainer(){};
ObjectGroupsContainer& operator=(const ObjectGroupsContainer& rhs);
/**
* \brief Return true if the specified group is in the container
@@ -171,8 +175,14 @@ class GD_CORE_API ObjectGroupsContainer {
const ObjectGroup& at(size_t index) const { return Get(index); };
///@}
/**
* Initialize from another object groups container. Used by copy-ctor and
* assign-op. Don't forget to update me if members were changed!
*/
void Init(const gd::ObjectGroupsContainer& other);
private:
std::vector<ObjectGroup> objectGroups;
std::vector<std::unique_ptr<gd::ObjectGroup>> objectGroups;
static ObjectGroup badGroup;
};

View File

@@ -4,7 +4,9 @@
* reserved. This project is released under the MIT License.
*/
#include "GDCore/Project/ObjectsContainer.h"
#include <algorithm>
#include "GDCore/Extensions/Platform.h"
#include "GDCore/Project/Object.h"
#include "GDCore/Project/Project.h"
@@ -16,14 +18,12 @@ ObjectsContainer::ObjectsContainer() {}
ObjectsContainer::~ObjectsContainer() {}
#if defined(GD_IDE_ONLY)
void ObjectsContainer::SerializeObjectsTo(SerializerElement& element) const {
element.ConsiderAsArrayOf("object");
for (std::size_t j = 0; j < initialObjects.size(); j++) {
initialObjects[j]->SerializeTo(element.AddChild("object"));
}
}
#endif
void ObjectsContainer::UnserializeObjectsFrom(
gd::Project& project, const SerializerElement& element) {
@@ -75,7 +75,6 @@ std::size_t ObjectsContainer::GetObjectPosition(const gd::String& name) const {
std::size_t ObjectsContainer::GetObjectsCount() const {
return initialObjects.size();
}
#if defined(GD_IDE_ONLY)
gd::Object& ObjectsContainer::InsertNewObject(const gd::Project& project,
const gd::String& objectType,
const gd::String& name,
@@ -87,7 +86,6 @@ gd::Object& ObjectsContainer::InsertNewObject(const gd::Project& project,
return newlyCreatedObject;
}
#endif
gd::Object& ObjectsContainer::InsertObject(const gd::Object& object,
std::size_t position) {
@@ -119,7 +117,7 @@ void ObjectsContainer::MoveObject(std::size_t oldIndex, std::size_t newIndex) {
}
void ObjectsContainer::RemoveObject(const gd::String& name) {
std::vector<std::unique_ptr<gd::Object> >::iterator objectIt =
std::vector<std::unique_ptr<gd::Object>>::iterator objectIt =
find_if(initialObjects.begin(),
initialObjects.end(),
bind2nd(ObjectHasName(), name));
@@ -132,7 +130,7 @@ void ObjectsContainer::MoveObjectToAnotherContainer(
const gd::String& name,
gd::ObjectsContainer& newContainer,
std::size_t newPosition) {
std::vector<std::unique_ptr<gd::Object> >::iterator objectIt =
std::vector<std::unique_ptr<gd::Object>>::iterator objectIt =
find_if(initialObjects.begin(),
initialObjects.end(),
bind2nd(ObjectHasName(), name));

View File

@@ -65,6 +65,8 @@ Project::Project()
pixelsRounding(false),
adaptGameResolutionAtRuntime(true),
sizeOnStartupMode("adaptWidth"),
antialiasingMode("MSAA"),
isAntialisingEnabledOnMobile(false),
projectUuid(""),
useDeprecatedZeroAsDefaultZOrder(false),
useExternalSourceFiles(false),
@@ -81,24 +83,23 @@ Project::~Project() {}
void Project::ResetProjectUuid() { projectUuid = UUID::MakeUuid4(); }
std::unique_ptr<gd::Object> Project::CreateObject(
const gd::String& type,
const gd::String& name) const {
return gd::make_unique<Object>(name, type, CreateObjectConfiguration(type));
const gd::String& type, const gd::String& name) const {
return gd::make_unique<Object>(name, type, CreateObjectConfiguration(type));
}
std::unique_ptr<gd::ObjectConfiguration> Project::CreateObjectConfiguration(
const gd::String& type) const {
const gd::String& type) const {
if (Project::HasEventsBasedObject(type)) {
return gd::make_unique<CustomObjectConfiguration>(*this, type);
}
else {
} else {
// Create a base object if the type can't be found in the platform.
return currentPlatform->CreateObjectConfiguration(type);
}
}
bool Project::HasEventsBasedObject(const gd::String& type) const {
const auto separatorIndex = type.find(PlatformExtension::GetNamespaceSeparator());
const auto separatorIndex =
type.find(PlatformExtension::GetNamespaceSeparator());
if (separatorIndex == std::string::npos) {
return false;
}
@@ -106,59 +107,67 @@ bool Project::HasEventsBasedObject(const gd::String& type) const {
if (!Project::HasEventsFunctionsExtensionNamed(extensionName)) {
return false;
}
auto &extension = Project::GetEventsFunctionsExtension(extensionName);
auto& extension = Project::GetEventsFunctionsExtension(extensionName);
gd::String objectTypeName = type.substr(separatorIndex + 2);
return extension.GetEventsBasedObjects().Has(objectTypeName);
}
gd::EventsBasedObject& Project::GetEventsBasedObject(const gd::String& type) {
const auto separatorIndex = type.find(PlatformExtension::GetNamespaceSeparator());
const auto separatorIndex =
type.find(PlatformExtension::GetNamespaceSeparator());
gd::String extensionName = type.substr(0, separatorIndex);
gd::String objectTypeName = type.substr(separatorIndex + 2);
auto &extension = Project::GetEventsFunctionsExtension(extensionName);
auto& extension = Project::GetEventsFunctionsExtension(extensionName);
return extension.GetEventsBasedObjects().Get(objectTypeName);
}
const gd::EventsBasedObject& Project::GetEventsBasedObject(const gd::String& type) const {
const auto separatorIndex = type.find(PlatformExtension::GetNamespaceSeparator());
const gd::EventsBasedObject& Project::GetEventsBasedObject(
const gd::String& type) const {
const auto separatorIndex =
type.find(PlatformExtension::GetNamespaceSeparator());
gd::String extensionName = type.substr(0, separatorIndex);
gd::String objectTypeName = type.substr(separatorIndex + 2);
const auto &extension = Project::GetEventsFunctionsExtension(extensionName);
const auto& extension = Project::GetEventsFunctionsExtension(extensionName);
return extension.GetEventsBasedObjects().Get(objectTypeName);
}
bool Project::HasEventsBasedBehavior(const gd::String& type) const {
const auto separatorIndex = type.find(PlatformExtension::GetNamespaceSeparator());
if (separatorIndex == std::string::npos) {
return false;
}
gd::String extensionName = type.substr(0, separatorIndex);
if (!Project::HasEventsFunctionsExtensionNamed(extensionName)) {
return false;
}
auto &extension = Project::GetEventsFunctionsExtension(extensionName);
gd::String behaviorTypeName = type.substr(separatorIndex + 2);
return extension.GetEventsBasedBehaviors().Has(behaviorTypeName);
const auto separatorIndex =
type.find(PlatformExtension::GetNamespaceSeparator());
if (separatorIndex == std::string::npos) {
return false;
}
gd::String extensionName = type.substr(0, separatorIndex);
if (!Project::HasEventsFunctionsExtensionNamed(extensionName)) {
return false;
}
auto& extension = Project::GetEventsFunctionsExtension(extensionName);
gd::String behaviorTypeName = type.substr(separatorIndex + 2);
return extension.GetEventsBasedBehaviors().Has(behaviorTypeName);
}
gd::EventsBasedBehavior& Project::GetEventsBasedBehavior(const gd::String& type) {
const auto separatorIndex = type.find(PlatformExtension::GetNamespaceSeparator());
gd::String extensionName = type.substr(0, separatorIndex);
gd::String behaviorTypeName = type.substr(separatorIndex + 2);
gd::EventsBasedBehavior& Project::GetEventsBasedBehavior(
const gd::String& type) {
const auto separatorIndex =
type.find(PlatformExtension::GetNamespaceSeparator());
gd::String extensionName = type.substr(0, separatorIndex);
gd::String behaviorTypeName = type.substr(separatorIndex + 2);
auto &extension = Project::GetEventsFunctionsExtension(extensionName);
return extension.GetEventsBasedBehaviors().Get(behaviorTypeName);
auto& extension = Project::GetEventsFunctionsExtension(extensionName);
return extension.GetEventsBasedBehaviors().Get(behaviorTypeName);
}
const gd::EventsBasedBehavior& Project::GetEventsBasedBehavior(const gd::String& type) const {
const auto separatorIndex = type.find(PlatformExtension::GetNamespaceSeparator());
gd::String extensionName = type.substr(0, separatorIndex);
gd::String behaviorTypeName = type.substr(separatorIndex + 2);
const gd::EventsBasedBehavior& Project::GetEventsBasedBehavior(
const gd::String& type) const {
const auto separatorIndex =
type.find(PlatformExtension::GetNamespaceSeparator());
gd::String extensionName = type.substr(0, separatorIndex);
gd::String behaviorTypeName = type.substr(separatorIndex + 2);
auto &extension = Project::GetEventsFunctionsExtension(extensionName);
return extension.GetEventsBasedBehaviors().Get(behaviorTypeName);
auto& extension = Project::GetEventsFunctionsExtension(extensionName);
return extension.GetEventsBasedBehaviors().Get(behaviorTypeName);
}
std::shared_ptr<gd::BaseEvent> Project::CreateEvent(
@@ -378,7 +387,7 @@ void Project::MoveExternalLayout(std::size_t oldIndex, std::size_t newIndex) {
};
void Project::MoveEventsFunctionsExtension(std::size_t oldIndex,
std::size_t newIndex) {
std::size_t newIndex) {
if (oldIndex >= eventsFunctionsExtensions.size() ||
newIndex >= eventsFunctionsExtensions.size())
return;
@@ -621,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"));
@@ -875,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);
@@ -1005,7 +1018,7 @@ void Project::ExposeResources(gd::ArbitraryResourceWorker& worker) {
// Add layouts resources
for (std::size_t s = 0; s < GetLayoutsCount(); s++) {
for (std::size_t j = 0; j < GetLayout(s).GetObjectsCount();
++j) { // Add objects resources
++j) { // Add objects resources
GetLayout(s).GetObject(j).GetConfiguration().ExposeResources(worker);
}
@@ -1106,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

@@ -147,7 +147,7 @@ module.exports = {
.addCondition(
'AppOpenErrored',
_('App open errored'),
_('Check if there was a error while loading the app open.'),
_('Check if there was an error while loading the app open.'),
_('App open had an error'),
'',
'JsPlatform/Extensions/admobicon24.png',

View File

@@ -606,7 +606,7 @@ module.exports = {
);
if (this._instance.hasCustomSize() && this._pixiObject) {
const customWidth = this._instance.getCustomWidth();
const customWidth = this.getCustomWidth();
if (
this._pixiObject &&
this._pixiObject._style.wordWrapWidth !== customWidth

View File

@@ -711,7 +711,7 @@ module.exports = {
const wordWrap = properties.get('wordWrap').getValue() === 'true';
if (wordWrap && this._instance.hasCustomSize()) {
this._pixiObject.maxWidth =
this._instance.getCustomWidth() / this._pixiObject.scale.x;
this.getCustomWidth() / this._pixiObject.scale.x;
this._pixiObject.dirty = true;
} else {
this._pixiObject.maxWidth = 0;

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

@@ -563,8 +563,8 @@ module.exports = {
this._pixiObject.rotation = RenderedInstance.toRad(
this._instance.getAngle()
);
// Custom size can be read in instance.getCustomWidth() and
// instance.getCustomHeight()
// Custom size can be read in this.getCustomWidth() and
// this.getCustomHeight()
};
/**

View File

@@ -41,7 +41,7 @@ namespace gdjs {
/**
* The current authentication status.
*/
export let authentified = false;
export let authenticated = false;
/**
* The logged-in users data.
@@ -345,10 +345,13 @@ namespace gdjs {
};
/**
* Returns true if the user is currently authentified.
* @see authentified
* Returns true if the user is currently authenticated.
* @see authenticated
*/
export const isAuthentified = (): boolean => authentified;
export const isAuthenticated = (): boolean => authenticated;
/** @deprecated Use isAuthenticated instead. */
export const isAuthentified = isAuthenticated;
/**
* Signs the user in with basic email-password authentication.
@@ -442,14 +445,14 @@ namespace gdjs {
firebaseTools.onAppCreated.push(() => {
firebase.auth().onAuthStateChanged((user) => {
if (user) {
authentified = true;
authenticated = true;
currentUser = user;
user.getIdToken().then(
// Prefetch the token
(token) => (_token = token)
);
} else {
authentified = false;
authenticated = false;
currentUser = null;
}
});

View File

@@ -14,7 +14,7 @@ namespace gdjs {
/**
* Uploads a file as string to the firebase storage bucket.
* @param file - The entire file as string.
* @param onlinePath - The path under wich the file will be accessible on the bucket.
* @param onlinePath - The path under which the file will be accessible on the bucket.
* @param [type] - The type/format of the string to upload.
* @param [callbackStateVariable] - The variable where to store if the operation was successful.
* @param [callbackValueVariable] - The variable where to store the result (url to the file).

View File

@@ -395,7 +395,7 @@ module.exports = {
.addIncludeFile('Extensions/Firebase/A_firebasejs/B_firebase-auth.js')
.addIncludeFile('Extensions/Firebase/B_firebasetools/C_firebasetools.js')
.addIncludeFile('Extensions/Firebase/B_firebasetools/D_authtools.js')
.setFunctionName('gdjs.evtTools.firebaseTools.auth.isAuthentified');
.setFunctionName('gdjs.evtTools.firebaseTools.auth.isAuthenticated');
extension
.addStrExpression(

View File

@@ -533,7 +533,7 @@ describeIfOnline('Firebase extension end-to-end tests', function () {
const password2 = `myNewPass${Math.random().toString(16)}${Date.now()}!`;
const expectToNotLogin = async (password) => {
if (gdjs.evtTools.firebaseTools.auth.isAuthentified())
if (gdjs.evtTools.firebaseTools.auth.isAuthenticated())
await firebase.auth().signOut();
let errors = false;
@@ -553,11 +553,11 @@ describeIfOnline('Firebase extension end-to-end tests', function () {
if (!errors)
throw new Error('Expected wrong credentials to prevent login');
expect(gdjs.evtTools.firebaseTools.auth.isAuthentified()).to.not.be.ok();
expect(gdjs.evtTools.firebaseTools.auth.isAuthenticated()).to.not.be.ok();
};
const expectToLogin = async (password) => {
if (gdjs.evtTools.firebaseTools.auth.isAuthentified())
if (gdjs.evtTools.firebaseTools.auth.isAuthenticated())
await firebase.auth().signOut();
await promisifyCallbackVariables((callback) =>
@@ -568,13 +568,13 @@ describeIfOnline('Firebase extension end-to-end tests', function () {
)
);
expect(gdjs.evtTools.firebaseTools.auth.isAuthentified()).to.be.ok();
expect(gdjs.evtTools.firebaseTools.auth.isAuthenticated()).to.be.ok();
};
before(async () => firebase.auth().signOut());
it('let users create accounts', async () => {
expect(gdjs.evtTools.firebaseTools.auth.isAuthentified()).to.not.be.ok();
expect(gdjs.evtTools.firebaseTools.auth.isAuthenticated()).to.not.be.ok();
await promisifyCallbackVariables((callback) =>
gdjs.evtTools.firebaseTools.auth.createAccountWithEmail(
@@ -584,13 +584,13 @@ describeIfOnline('Firebase extension end-to-end tests', function () {
)
);
expect(gdjs.evtTools.firebaseTools.auth.isAuthentified()).to.be.ok();
expect(gdjs.evtTools.firebaseTools.auth.isAuthenticated()).to.be.ok();
});
it('let users log out', async () => {
expect(gdjs.evtTools.firebaseTools.auth.isAuthentified()).to.be.ok();
expect(gdjs.evtTools.firebaseTools.auth.isAuthenticated()).to.be.ok();
await firebase.auth().signOut();
expect(gdjs.evtTools.firebaseTools.auth.isAuthentified()).to.not.be.ok();
expect(gdjs.evtTools.firebaseTools.auth.isAuthenticated()).to.not.be.ok();
});
it('prevents logging in with invalid credentials', async () =>

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

@@ -758,7 +758,7 @@ module.exports = {
aut
.addAction(
'SetSleepingaAllowed',
'SetSleepingAllowed',
_('Sleeping allowed'),
_(
'Allow or not an object to sleep. If enabled the object will be able to sleep, improving performance for non-currently-moving objects.'
@@ -774,6 +774,11 @@ module.exports = {
.setDefaultValue('true')
.getCodeExtraInformation()
.setFunctionName('setSleepingAllowed');
// Deprecated action (fixed typo):
aut
.addDuplicatedAction("SetSleepingaAllowed", "SetSleepingAllowed")
.setHidden();
aut
.addCondition(

View File

@@ -140,7 +140,7 @@ namespace gdjs {
} else if (behaviorData.platformType === 'Jumpthru') {
this._platformType = PlatformRuntimeBehavior.JUMPTHRU;
} else {
this._platformType = PlatformRuntimeBehavior.NORMALPLAFTORM;
this._platformType = PlatformRuntimeBehavior.NORMALPLATFORM;
}
this._canBeGrabbed = behaviorData.canBeGrabbed || false;
this._yGrabOffset = behaviorData.yGrabOffset || 0;
@@ -232,7 +232,7 @@ namespace gdjs {
} else if (platformType === 'Jumpthru') {
this._platformType = PlatformRuntimeBehavior.JUMPTHRU;
} else {
this._platformType = PlatformRuntimeBehavior.NORMALPLAFTORM;
this._platformType = PlatformRuntimeBehavior.NORMALPLATFORM;
}
}
@@ -248,7 +248,9 @@ namespace gdjs {
return this._yGrabOffset;
}
static NORMALPLAFTORM = 0;
static NORMALPLATFORM = 0;
/** @deprecated Use NORMALPLATFORM instead. */
static NORMALPLAFTORM = PlatformRuntimeBehavior.NORMALPLATFORM;
static JUMPTHRU = 1;
static LADDER = 2;

View File

@@ -598,8 +598,8 @@ namespace gdjs {
break;
}
case 'connectionId': {
const messagegeData = messageContent.data;
const connectionId = messagegeData.connectionId;
const messageData = messageContent.data;
const connectionId = messageData.connectionId;
if (!connectionId) {
logger.error('No connectionId received');
return;

View File

@@ -239,6 +239,7 @@ namespace gdjs {
}
beginFillPath() {
this.updateOutline();
this._graphics.beginFill(
this._object._fillColor,
this._object._fillOpacity / 255

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,63 @@
namespace gdjs {
export namespace steamworks {
gdjs.registerFirstRuntimeSceneLoadedCallback(() => {
if (gdjs.steamworks.steamAPI) gdjs.steamworks.steamAPI.input.init();
});
export function getControllerCount(): integer {
return gdjs.steamworks.steamAPI
? gdjs.steamworks.steamAPI.input.getControllers().length
: 0;
}
export function activateActionSet(
controllerIndex: number,
actionSetName: string
): void {
if (!gdjs.steamworks.steamAPI) return;
gdjs.steamworks.steamAPI.input
.getControllers()
[controllerIndex]?.activateActionSet(
gdjs.steamworks.steamAPI.input.getActionSet(actionSetName)
);
}
export function isDigitalActionPressed(
controllerIndex: number,
actionName: string
): boolean {
if (!gdjs.steamworks.steamAPI) return false;
return !!gdjs.steamworks.steamAPI.input
.getControllers()
[controllerIndex]?.isDigitalActionPressed(
gdjs.steamworks.steamAPI.input.getDigitalAction(actionName)
);
}
export function getAnalogActionVectorX(
controllerIndex: number,
actionName: string
): float {
return gdjs.steamworks.steamAPI
? gdjs.steamworks.steamAPI.input
.getControllers()
[controllerIndex]?.getAnalogActionVector(
gdjs.steamworks.steamAPI.input.getAnalogAction(actionName)
).x ?? 0
: 0;
}
export function getAnalogActionVectorY(
controllerIndex: number,
actionName: string
): float {
return gdjs.steamworks.steamAPI
? gdjs.steamworks.steamAPI.input
.getControllers()
[controllerIndex]?.getAnalogActionVector(
gdjs.steamworks.steamAPI.input.getAnalogAction(actionName)
).y ?? 0
: 0;
}
}
}

View File

@@ -0,0 +1,566 @@
namespace gdjs {
export namespace steamworks {
const logger = new gdjs.Logger('Steamworks');
export let steamAPI: import('steamworks.js').Client | null = null;
gdjs.registerFirstRuntimeSceneLoadedCallback((runtimeScene) => {
const remote = runtimeScene.getGame().getRenderer().getElectronRemote();
if (!remote) return; // Steamworks is only supported on electron
const steamworks_js = remote.require(
'steamworks.js'
) as typeof import('steamworks.js');
// Sets the proper electron flags for the steam overlay to function properly
steamworks_js.electronEnableSteamOverlay();
const unparsedAppID = runtimeScene
.getGame()
.getExtensionProperty('Steamworks', 'AppID');
if (!unparsedAppID) {
logger.error(
'A steam AppID needs to be configured in the game properties for steamworks features to be used!'
);
return;
}
const appID = parseInt(unparsedAppID, 10);
// Restart the game through steam if it needs to be launched with steam but has not been
if (
runtimeScene
.getGame()
.getExtensionProperty('Steamworks', 'RequireSteam') &&
!runtimeScene.getGame().isPreview() &&
steamworks_js.restartAppIfNecessary(appID)
) {
remote.process.exit(1);
return;
}
steamAPI = steamworks_js.init(appID);
});
// ---
export function claimAchievement(achievement: string): void {
if (steamAPI) steamAPI.achievement.activate(achievement);
else
logger.warn(
`Could not claim achievement ${achievement}, steamworks was not properly loaded!`
);
}
export function unclaimAchievement(achievement: string): void {
if (steamAPI) steamAPI.achievement.clear(achievement);
else
logger.warn(
`Could not unclaim achievement ${achievement}, steamworks was not properly loaded!`
);
}
export function hasAchievement(achievement: string): boolean {
return !!steamAPI && steamAPI.achievement.isActivated(achievement);
}
// ---
export function getSteamId(): string {
return steamAPI
? steamAPI.localplayer.getSteamId().steamId64.toString(10)
: '';
}
export function getName(): string {
return steamAPI ? steamAPI.localplayer.getName() : 'Unknown';
}
export function getCountry(): string {
return steamAPI ? steamAPI.localplayer.getIpCountry() : 'Unknown';
}
export function getLevel(): number {
return steamAPI ? steamAPI.localplayer.getLevel() : 0;
}
export function setRichPresence(key: string, value: string): void {
if (steamAPI) steamAPI.localplayer.setRichPresence(key, value);
else
logger.warn(
`Could not set the rich presence, steamworks was not properly loaded!`
);
}
// ---
export function isSteamworksProperlyLoaded(): boolean {
return !!steamAPI;
}
export function getAppID(): number {
return steamAPI ? steamAPI.utils.getAppId() : 0;
}
export function getServerRealTime(): number {
return steamAPI ? steamAPI.utils.getServerRealTime() : Date.now();
}
export function isOnSteamDeck(): boolean {
return steamAPI ? steamAPI.utils.isSteamRunningOnSteamDeck() : false;
}
// ---
enum LobbyType {
Private = 0,
FriendsOnly = 1,
Public = 2,
Invisible = 3,
}
const knownLobbies = new Map<
string,
import('steamworks.js/client').matchmaking.Lobby
>();
let currentLobby:
| import('steamworks.js/client').matchmaking.Lobby
| null = null;
export function getKnownLobby(lobbyId: string) {
if (!steamAPI) {
logger.warn(
`Could not access lobby '${lobbyId}', steamworks was not properly loaded!`
);
return null;
}
const lobby = knownLobbies.get(lobbyId);
if (!lobby) {
logger.error(
`Could not access lobby '${lobbyId}'! You might need to join it before trying to access it.`
);
return null;
}
return lobby;
}
export function createLobby(
lobbyType: 'Private' | 'FriendsOnly' | 'Public' | 'Invisible',
maxPlayers: number,
result: gdjs.Variable
): gdjs.AsyncTask {
if (steamAPI) {
return new gdjs.PromiseTask(
steamAPI.matchmaking
.createLobby(
LobbyType[lobbyType] as any /* Const enums are 😩 */,
maxPlayers
)
.then((lobby) => {
const id = lobby.id.toString(10);
knownLobbies.set(id, lobby);
currentLobby = lobby;
result.setString(id);
})
.catch(() => {
result.setString('failure');
})
);
} else {
logger.warn(
`Could not create a lobby, steamworks was not properly loaded!`
);
return new gdjs.ResolveTask();
}
}
export function getLobbiesList(results: gdjs.Variable): gdjs.AsyncTask {
if (steamAPI) {
return new gdjs.PromiseTask(
steamAPI.matchmaking
.getLobbies()
.then((lobbies) => {
const allLobbiesIds = lobbies.map((lobby) => {
const id = lobby.id.toString(10);
knownLobbies.set(id, lobby);
return id;
});
results.fromJSObject(allLobbiesIds);
})
.catch(() => {
results.setString('failure');
})
);
} else {
logger.warn(
`Could not obtain the lobbies list, steamworks was not properly loaded!`
);
return new gdjs.ResolveTask();
}
}
export function joinLobby(
lobbyId: string,
result: gdjs.Variable
): gdjs.AsyncTask {
if (steamAPI) {
return new gdjs.PromiseTask(
steamAPI.matchmaking
.joinLobby(BigInt(lobbyId))
.then((lobby) => {
knownLobbies.set(lobbyId, lobby);
currentLobby = lobby;
result.setString(lobbyId);
})
.catch(() => {
result.setString('failure');
})
);
} else {
logger.warn(
`Could not join a lobby, steamworks was not properly loaded!`
);
return new gdjs.ResolveTask();
}
}
export function getCurrentLobbyId(): string {
return currentLobby ? currentLobby.id.toString(10) : 'none';
}
export function leaveCurrentLobby(): void {
if (currentLobby) currentLobby.leave();
}
export function openDialogForInvitingUsersToTheCurrentLobby(): void {
if (currentLobby) currentLobby.openInviteDialog();
}
export function getCurrentLobbyAttribute(attribute: string): string {
if (!currentLobby) return '';
const data = currentLobby.getData(attribute);
return data === null ? '' : data;
}
export function getLobbyAttribute(
lobbyId: string,
attribute: string
): string {
const lobby = getKnownLobby(lobbyId);
if (!lobby) return '';
const data = lobby.getData(attribute);
return data === null ? '' : data;
}
export function setCurrentLobbyAttribute(
attribute: string,
value: string,
success: gdjs.Variable
): void {
if (currentLobby)
success.setBoolean(currentLobby.setData(attribute, value));
}
export function setCurrentLobbyJoinability(
shouldBeJoinable: boolean,
success: gdjs.Variable
): void {
if (currentLobby)
success.setBoolean(currentLobby.setJoinable(shouldBeJoinable));
}
export function getCurrentLobbyMemberCount(): number {
return currentLobby ? Number(currentLobby.getMemberCount()) : 0;
}
export function getLobbyMemberCount(lobbyId: string): number {
const lobby = getKnownLobby(lobbyId);
return lobby ? Number(lobby.getMemberCount()) : 0;
}
export function getCurrentLobbyMemberLimit(): number {
return currentLobby ? Number(currentLobby.getMemberLimit()) : 0;
}
export function getLobbyMemberLimit(lobbyId: string): number {
const lobby = getKnownLobby(lobbyId);
if (!lobby) return 0;
return lobby ? Number(lobby.getMemberLimit()) : 0;
}
export function getCurrentLobbyOwner(): string {
return currentLobby ? currentLobby.getOwner().steamId64.toString(10) : '';
}
export function getLobbyOwner(lobbyId: string): string {
const lobby = getKnownLobby(lobbyId);
return lobby ? lobby.getOwner().steamId64.toString(10) : '';
}
export function getCurrentLobbyMembersList(storeIn: gdjs.Variable): void {
if (currentLobby) {
storeIn.fromJSObject(
currentLobby
.getMembers()
.map((steamID) => steamID.steamId64.toString(10))
);
}
}
export function getLobbyMembersList(
lobbyId: string,
storeIn: gdjs.Variable
): void {
const lobby = getKnownLobby(lobbyId);
if (lobby) {
storeIn.fromJSObject(
lobby.getMembers().map((steamID) => steamID.steamId64.toString(10))
);
}
}
// ---
export function isAppOwned(appId: string): boolean {
return !!steamAPI && steamAPI.apps.isSubscribedApp(Number(appId));
}
export function isAppInstalled(appId: string): boolean {
return !!steamAPI && steamAPI.apps.isAppInstalled(Number(appId));
}
export function isDLCInstalled(dlcId: string): boolean {
return !!steamAPI && steamAPI.apps.isDlcInstalled(Number(dlcId));
}
export function getAppInstallDirectory(appId: string): string {
return steamAPI ? steamAPI.apps.appInstallDir(Number(appId)) : '';
}
export function isVacBanned(): boolean {
return !!steamAPI && steamAPI.apps.isVacBanned();
}
export function isLowViolence(): boolean {
return !!steamAPI && steamAPI.apps.isLowViolence();
}
export function userBoughtTheGame(): boolean {
return !!steamAPI && steamAPI.apps.isSubscribed();
}
export function currentGameLanguage(): string {
return steamAPI ? steamAPI.apps.currentGameLanguage() : '';
}
export function currentBetaName(): string {
return steamAPI ? steamAPI.apps.currentBetaName() || '' : '';
}
export function getBuildId(): number {
return steamAPI ? steamAPI.apps.appBuildId() : 0;
}
// ---
export function isCloudEnabled(): boolean {
return (
!!steamAPI &&
steamAPI.cloud.isEnabledForAccount() &&
steamAPI.cloud.isEnabledForApp()
);
}
export function readFile(fileName: string): string {
return steamAPI ? steamAPI.cloud.readFile(fileName) : '';
}
export function writeFile(
fileName: string,
content: string,
results: gdjs.Variable
): void {
if (steamAPI)
results.setBoolean(steamAPI.cloud.writeFile(fileName, content));
}
export function fileExists(fileName: string): boolean {
return steamAPI ? steamAPI.cloud.fileExists(fileName) : false;
}
export function deleteFile(fileName: string, results: gdjs.Variable): void {
if (steamAPI) results.setBoolean(steamAPI.cloud.deleteFile(fileName));
}
// ---
export function createWorkshopItem(result: gdjs.Variable): gdjs.AsyncTask {
if (steamAPI)
return new gdjs.PromiseTask(
steamAPI.workshop
.createItem()
.then(({ itemId }) => {
result.setString(itemId.toString());
})
.catch(() => {
result.setString('failure');
})
);
else {
result.setString('failure');
return new gdjs.ResolveTask();
}
}
enum UgcItemVisibility {
Public = 0,
FriendsOnly = 1,
Private = 2,
Unlisted = 3,
}
export function updateWorkshopItem(
itemId: string,
title: string,
description: string,
changeNote: string,
previewPath: string,
contentPath: string,
tags: string,
visibility: keyof import('steamworks.js/client').workshop.UgcItemVisibility,
result: gdjs.Variable
): gdjs.AsyncTask {
if (steamAPI) {
const changes: import('steamworks.js/client').workshop.UgcUpdate = {};
if (title) changes.title = title;
if (description) changes.description = description;
if (changeNote) changes.changeNote = changeNote;
if (previewPath) changes.previewPath = previewPath;
if (contentPath) changes.contentPath = contentPath;
if (tags) changes.tags = tags.split(',');
if (visibility)
//@ts-ignore const enum 😩
changes.visibility = UgcItemVisibility[visibility];
return new gdjs.PromiseTask(
steamAPI.workshop
.updateItem(BigInt(itemId), changes)
.then(() => {
result.setBoolean(true);
})
.catch(() => {
result.setBoolean(false);
})
);
} else {
result.setBoolean(false);
return new gdjs.ResolveTask();
}
}
export function subscribeToWorkshopItem(
itemId: string,
result: gdjs.Variable
): gdjs.AsyncTask {
if (steamAPI)
return new gdjs.PromiseTask(
steamAPI.workshop
.subscribe(BigInt(itemId))
.then(() => {
result.setBoolean(true);
})
.catch(() => {
result.setBoolean(false);
})
);
else {
result.setBoolean(false);
return new gdjs.ResolveTask();
}
}
export function unsubscribeToWorkshopItem(
itemId: string,
result: gdjs.Variable
): gdjs.AsyncTask {
if (steamAPI)
return new gdjs.PromiseTask(
steamAPI.workshop
.unsubscribe(BigInt(itemId))
.then(() => {
result.setBoolean(true);
})
.catch(() => {
result.setBoolean(false);
})
);
else {
result.setBoolean(false);
return new gdjs.ResolveTask();
}
}
export function startWorkshopDownload(
itemId: string,
highPriority: boolean
): void {
if (steamAPI) steamAPI.workshop.download(BigInt(itemId), highPriority);
}
enum WorkshopItemStates {
None = 0,
Subscribed = 1,
LegacyItem = 2,
Installed = 4,
NeedsUpdate = 8,
Downloading = 16,
DownloadPending = 32,
}
export function workshopItemState(
itemId: string,
state: keyof WorkshopItemStates
): boolean {
return (
!!steamAPI &&
(steamAPI.workshop.state(BigInt(itemId)) &
WorkshopItemStates[state]) !==
0
);
}
export function getWorkshopItemLocation(itemId: string): string {
return steamAPI
? steamAPI.workshop.installInfo(BigInt(itemId))?.folder || ''
: '';
}
export function getWorkshopItemSizeOnDisk(itemId: string): number {
return steamAPI
? Number(steamAPI.workshop.installInfo(BigInt(itemId))?.sizeOnDisk) || 0
: 0;
}
export function getWorkshopItemInstallTimestamp(itemId: string): number {
return steamAPI
? steamAPI.workshop.installInfo(BigInt(itemId))?.timestamp || 0
: 0;
}
export function getWorkshopItemDownloadProgress(itemId: string): number {
return steamAPI
? Number(steamAPI.workshop.downloadInfo(BigInt(itemId))?.current) || 0
: 0;
}
export function getWorkshopItemDownloadTotal(itemId: string): number {
return steamAPI
? Number(steamAPI.workshop.downloadInfo(BigInt(itemId))?.total) || 0
: 0;
}
}
}

362
Extensions/Steamworks/types.d.ts vendored Normal file
View File

@@ -0,0 +1,362 @@
declare module 'steamworks.js/client' {
export function init(appId: number): void;
export function restartAppIfNecessary(appId: number): boolean;
export function runCallbacks(): void;
export interface PlayerSteamId {
steamId64: bigint;
steamId32: string;
accountId: number;
}
export namespace achievement {
export function activate(achievement: string): boolean;
export function isActivated(achievement: string): boolean;
export function clear(achievement: string): boolean;
}
export namespace apps {
export function isSubscribedApp(appId: number): boolean;
export function isAppInstalled(appId: number): boolean;
export function isDlcInstalled(appId: number): boolean;
export function isSubscribedFromFreeWeekend(): boolean;
export function isVacBanned(): boolean;
export function isCybercafe(): boolean;
export function isLowViolence(): boolean;
export function isSubscribed(): boolean;
export function appBuildId(): number;
export function appInstallDir(appId: number): string;
export function appOwner(): PlayerSteamId;
export function availableGameLanguages(): Array<string>;
export function currentGameLanguage(): string;
export function currentBetaName(): string | null;
}
export namespace auth {
/** @param timeoutSeconds - The number of seconds to wait for the ticket to be validated. Default value is 10 seconds. */
export function getSessionTicket(
timeoutSeconds?: number | undefined | null
): Promise<Ticket>;
export class Ticket {
cancel(): void;
getBytes(): Buffer;
}
}
export const enum ChatMemberStateChange {
/** This user has joined or is joining the lobby. */
Entered,
/** This user has left or is leaving the lobby. */
Left,
/** User disconnected without leaving the lobby first. */
Disconnected,
/** The user has been kicked. */
Kicked,
/** The user has been kicked and banned. */
Banned,
}
export interface CallbackReturns {
[callback.SteamCallback.PersonaStateChange]: {
steam_id: bigint;
flags: { bits: number };
};
[callback.SteamCallback.SteamServersConnected]: {};
[callback.SteamCallback.SteamServersDisconnected]: {
reason: number;
};
[callback.SteamCallback.SteamServerConnectFailure]: {
reason: number;
still_retrying: boolean;
};
[callback.SteamCallback.LobbyDataUpdate]: {
lobby: bigint;
member: bigint;
success: boolean;
};
[callback.SteamCallback.LobbyChatUpdate]: {
lobby: bigint;
user_changed: bigint;
making_change: bigint;
member_state_change: ChatMemberStateChange;
};
[callback.SteamCallback.P2PSessionRequest]: {
remote: bigint;
};
[callback.SteamCallback.P2PSessionConnectFail]: {
remote: bigint;
error: number;
};
[callback.SteamCallback.GameLobbyJoinRequested]: {
lobby_steam_id: bigint;
friend_steam_id: bigint;
};
[callback.SteamCallback.MicroTxnAuthorizationResponse]: {
app_id: number;
order_id: number | bigint;
authorized: boolean;
};
}
export namespace callback {
export const enum SteamCallback {
PersonaStateChange = 0,
SteamServersConnected = 1,
SteamServersDisconnected = 2,
SteamServerConnectFailure = 3,
LobbyDataUpdate = 4,
LobbyChatUpdate = 5,
P2PSessionRequest = 6,
P2PSessionConnectFail = 7,
GameLobbyJoinRequested = 8,
MicroTxnAuthorizationResponse = 9,
}
export function register<C extends keyof CallbackReturns>(
steamCallback: C,
handler: (value: CallbackReturns[C]) => void
): Handle;
export class Handle {
disconnect(): void;
}
}
export namespace cloud {
export function isEnabledForAccount(): boolean;
export function isEnabledForApp(): boolean;
export function readFile(name: string): string;
export function writeFile(name: string, content: string): boolean;
export function deleteFile(name: string): boolean;
export function fileExists(name: string): boolean;
}
export namespace input {
export interface AnalogActionVector {
x: number;
y: number;
}
export function init(): void;
export function getControllers(): Array<Controller>;
export function getActionSet(actionSetName: string): bigint;
export function getDigitalAction(actionName: string): bigint;
export function getAnalogAction(actionName: string): bigint;
export function shutdown(): void;
export class Controller {
activateActionSet(actionSetHandle: bigint): void;
isDigitalActionPressed(actionHandle: bigint): boolean;
getAnalogActionVector(actionHandle: bigint): AnalogActionVector;
}
}
export namespace localplayer {
export function getSteamId(): PlayerSteamId;
export function getName(): string;
export function getLevel(): number;
/** @returns the 2 digit ISO 3166-1-alpha-2 format country code which client is running in, e.g. "US" or "UK". */
export function getIpCountry(): string;
export function setRichPresence(
key: string,
value?: string | undefined | null
): void;
}
export namespace matchmaking {
export const enum LobbyType {
Private = 0,
FriendsOnly = 1,
Public = 2,
Invisible = 3,
}
export function createLobby(
lobbyType: LobbyType,
maxMembers: number
): Promise<Lobby>;
export function joinLobby(lobbyId: bigint): Promise<Lobby>;
export function getLobbies(): Promise<Array<Lobby>>;
export class Lobby {
id: bigint;
join(): Promise<Lobby>;
leave(): void;
openInviteDialog(): void;
getMemberCount(): bigint;
getMemberLimit(): bigint | null;
getMembers(): Array<PlayerSteamId>;
getOwner(): PlayerSteamId;
setJoinable(joinable: boolean): boolean;
getData(key: string): string | null;
setData(key: string, value: string): boolean;
deleteData(key: string): boolean;
/** Get an object containing all the lobby data */
getFullData(): Record<string, string>;
/**
* Merge current lobby data with provided data in a single batch
* @returns true if all data was set successfully
*/
mergeFullData(data: Record<string, string>): boolean;
}
}
export namespace networking {
export interface P2PPacket {
data: Buffer;
size: number;
steamId: PlayerSteamId;
}
/** The method used to send a packet */
export const enum SendType {
/**
* Send the packet directly over udp.
*
* Can't be larger than 1200 bytes
*/
Unreliable = 0,
/**
* Like `Unreliable` but doesn't buffer packets
* sent before the connection has started.
*/
UnreliableNoDelay = 1,
/**
* Reliable packet sending.
*
* Can't be larger than 1 megabyte.
*/
Reliable = 2,
/**
* Like `Reliable` but applies the nagle
* algorithm to packets being sent
*/
ReliableWithBuffering = 3,
}
export function sendP2PPacket(
steamId64: bigint,
sendType: SendType,
data: Buffer
): boolean;
export function isP2PPacketAvailable(): number;
export function readP2PPacket(size: number): P2PPacket;
export function acceptP2PSession(steamId64: bigint): void;
}
export namespace overlay {
export const enum Dialog {
Friends = 0,
Community = 1,
Players = 2,
Settings = 3,
OfficialGameGroup = 4,
Stats = 5,
Achievements = 6,
}
export const enum StoreFlag {
None = 0,
AddToCart = 1,
AddToCartAndShow = 2,
}
export function activateDialog(dialog: Dialog): void;
export function activateDialogToUser(
dialog: Dialog,
steamId64: bigint
): void;
export function activateInviteDialog(lobbyId: bigint): void;
export function activateToWebPage(url: string): void;
export function activateToStore(appId: number, flag: StoreFlag): void;
}
export namespace stats {
export function getInt(name: string): number | null;
export function setInt(name: string, value: number): boolean;
export function store(): boolean;
export function resetAll(achievementsToo: boolean): boolean;
}
export namespace utils {
export function getAppId(): number;
export function getServerRealTime(): number;
export function isSteamRunningOnSteamDeck(): boolean;
}
export namespace workshop {
export interface UgcResult {
itemId: bigint;
needsToAcceptAgreement: boolean;
}
export const enum UgcItemVisibility {
Public = 0,
FriendsOnly = 1,
Private = 2,
Unlisted = 3,
}
export interface UgcUpdate {
title?: string;
description?: string;
changeNote?: string;
previewPath?: string;
contentPath?: string;
tags?: Array<string>;
visibility?: UgcItemVisibility;
}
export interface InstallInfo {
folder: string;
sizeOnDisk: bigint;
timestamp: number;
}
export interface DownloadInfo {
current: bigint;
total: bigint;
}
export function createItem(
appId?: number | undefined | null
): Promise<UgcResult>;
export function updateItem(
itemId: bigint,
updateDetails: UgcUpdate,
appId?: number | undefined | null
): Promise<UgcResult>;
/**
* Subscribe to a workshop item. It will be downloaded and installed as soon as possible.
*
* {@link https://partner.steamgames.com/doc/api/ISteamUGC#SubscribeItem}
*/
export function subscribe(itemId: bigint): Promise<void>;
/**
* Unsubscribe from a workshop item. This will result in the item being removed after the game quits.
*
* {@link https://partner.steamgames.com/doc/api/ISteamUGC#UnsubscribeItem}
*/
export function unsubscribe(itemId: bigint): Promise<void>;
/**
* Gets the current state of a workshop item on this client. States can be combined.
*
* @returns a number with the current item state, e.g. 9
* 9 = 1 (The current user is subscribed to this item) + 8 (The item needs an update)
*
* {@link https://partner.steamgames.com/doc/api/ISteamUGC#GetItemState}
* {@link https://partner.steamgames.com/doc/api/ISteamUGC#EItemState}
*/
export function state(itemId: bigint): number;
/**
* Gets info about currently installed content on the disc for workshop item.
*
* @returns an object with the the properties {folder, size_on_disk, timestamp}
*
* {@link https://partner.steamgames.com/doc/api/ISteamUGC#GetItemInstallInfo}
*/
export function installInfo(itemId: bigint): InstallInfo | null;
/**
* Get info about a pending download of a workshop item.
*
* @returns an object with the properties {current, total}
*
* {@link https://partner.steamgames.com/doc/api/ISteamUGC#GetItemDownloadInfo}
*/
export function downloadInfo(itemId: bigint): DownloadInfo | null;
/**
* Download or update a workshop item.
*
* @param highPriority - If high priority is true, start the download in high priority mode, pausing any existing in-progress Steam downloads and immediately begin downloading this workshop item.
* @returns true or false
*
* {@link https://partner.steamgames.com/doc/api/ISteamUGC#DownloadItem}
*/
export function download(itemId: bigint, highPriority: boolean): boolean;
}
}
declare module 'steamworks.js' {
export function init(appId?: number): Client;
export function restartAppIfNecessary(appId: number): boolean;
export function electronEnableSteamOverlay(
disableEachFrameInvalidation?: boolean
): void;
export type Client = Omit<
typeof import('steamworks.js/client'),
'init' | 'runCallbacks'
>;
export const SteamCallback: typeof import('steamworks.js/client').callback.SteamCallback;
}

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

@@ -714,8 +714,8 @@ module.exports = {
let width = DEFAULT_WIDTH;
let height = DEFAULT_HEIGHT;
if (instance.hasCustomSize()) {
width = instance.getCustomWidth();
height = instance.getCustomHeight();
width = this.getCustomWidth();
height = this.getCustomHeight();
}
this._pixiObject.pivot.x = width / 2;

View File

@@ -387,7 +387,7 @@ const defineTileMap = function (
)
.addParameter('object', _('Tile map'), 'TileMap', false)
.useStandardParameters('number', gd.ParameterOptions.makeNewOptions())
.setFunctionName('getLevelndex');
.setFunctionName('getLevelIndex');
object
.addCondition(
@@ -1347,9 +1347,9 @@ module.exports = {
*/
RenderedTileMapInstance.prototype.update = function () {
if (this._instance.hasCustomSize()) {
this._pixiObject.scale.x = this._instance.getCustomWidth() / this.width;
this._pixiObject.scale.x = this.getCustomWidth() / this.width;
this._pixiObject.scale.y =
this._instance.getCustomHeight() / this.height;
this.getCustomHeight() / this.height;
} else {
this._pixiObject.scale.x = 1;
this._pixiObject.scale.y = 1;
@@ -1531,7 +1531,7 @@ module.exports = {
this._loadTiledMapWithCallback.bind(this),
tilemapJsonFile,
tilesetJsonFile,
0, // leveIndex
0, // levelIndex
pako,
(tileMap) => {
if (!tileMap) {
@@ -1599,9 +1599,9 @@ module.exports = {
*/
RenderedCollisionMaskInstance.prototype.update = function () {
if (this._instance.hasCustomSize()) {
this._pixiObject.scale.x = this._instance.getCustomWidth() / this.width;
this._pixiObject.scale.x = this.getCustomWidth() / this.width;
this._pixiObject.scale.y =
this._instance.getCustomHeight() / this.height;
this.getCustomHeight() / this.height;
} else {
this._pixiObject.scale.x = 1;
this._pixiObject.scale.y = 1;

View File

@@ -362,7 +362,7 @@ namespace gdjs {
destroyObjectWhenFinished: boolean
) {
const { owner } = this;
if (!(owner instanceof gdjs.Cube3DRuntimeObject)) return;
if (!(owner instanceof gdjs.RuntimeObject3D)) return;
this._addTween(
identifier,

View File

@@ -664,8 +664,8 @@ module.exports = {
);
if (this._instance.hasCustomSize()) {
this._pixiObject.width = this._instance.getCustomWidth();
this._pixiObject.height = this._instance.getCustomHeight();
this._pixiObject.width = this.getCustomWidth();
this._pixiObject.height = this.getCustomHeight();
}
};

View File

@@ -684,15 +684,15 @@ gd::String EventsCodeGenerator::GenerateFreeCondition(
bool conditionInverted,
gd::EventsCodeGenerationContext& context) {
// Generate call
gd::String predicat;
gd::String predicate;
if (instrInfos.codeExtraInformation.type == "number" ||
instrInfos.codeExtraInformation.type == "string") {
predicat = GenerateRelationalOperatorCall(
predicate = GenerateRelationalOperatorCall(
instrInfos,
arguments,
instrInfos.codeExtraInformation.functionCallName);
} else {
predicat = instrInfos.codeExtraInformation.functionCallName + "(" +
predicate = instrInfos.codeExtraInformation.functionCallName + "(" +
GenerateArgumentsList(arguments) + ")";
}
@@ -705,11 +705,11 @@ gd::String EventsCodeGenerator::GenerateFreeCondition(
conditionAlreadyTakeCareOfInversion = true;
}
if (!conditionAlreadyTakeCareOfInversion && conditionInverted)
predicat = GenerateNegatedPredicat(predicat);
predicate = GenerateNegatedPredicate(predicate);
// Generate condition code
return GenerateBooleanFullName(returnBoolean, context) +
" = " + predicat + ";\n";
" = " + predicate + ";\n";
}
gd::String EventsCodeGenerator::GenerateObjectCondition(
@@ -728,22 +728,22 @@ gd::String EventsCodeGenerator::GenerateObjectCondition(
instrInfos.codeExtraInformation.functionCallName;
// Create call
gd::String predicat;
gd::String predicate;
if ((instrInfos.codeExtraInformation.type == "number" ||
instrInfos.codeExtraInformation.type == "string")) {
predicat = GenerateRelationalOperatorCall(
predicate = GenerateRelationalOperatorCall(
instrInfos, arguments, objectFunctionCallNamePart, 1);
} else {
predicat = objectFunctionCallNamePart + "(" +
predicate = objectFunctionCallNamePart + "(" +
GenerateArgumentsList(arguments, 1) + ")";
}
if (conditionInverted) predicat = GenerateNegatedPredicat(predicat);
if (conditionInverted) predicate = GenerateNegatedPredicate(predicate);
// Generate whole condition code
conditionCode +=
"for (var i = 0, k = 0, l = " + GetObjectListName(objectName, context) +
".length;i<l;++i) {\n";
conditionCode += " if ( " + predicat + " ) {\n";
conditionCode += " if ( " + predicate + " ) {\n";
conditionCode += " " +
GenerateBooleanFullName(returnBoolean, context) +
" = true;\n";
@@ -775,16 +775,16 @@ gd::String EventsCodeGenerator::GenerateBehaviorCondition(
instrInfos.codeExtraInformation.functionCallName;
// Create call
gd::String predicat;
gd::String predicate;
if ((instrInfos.codeExtraInformation.type == "number" ||
instrInfos.codeExtraInformation.type == "string")) {
predicat = GenerateRelationalOperatorCall(
predicate = GenerateRelationalOperatorCall(
instrInfos, arguments, objectFunctionCallNamePart, 2);
} else {
predicat = objectFunctionCallNamePart + "(" +
predicate = objectFunctionCallNamePart + "(" +
GenerateArgumentsList(arguments, 2) + ")";
}
if (conditionInverted) predicat = GenerateNegatedPredicat(predicat);
if (conditionInverted) predicate = GenerateNegatedPredicate(predicate);
// Verify that object has behavior.
vector<gd::String> behaviors = gd::GetBehaviorsOfObject(
@@ -798,7 +798,7 @@ gd::String EventsCodeGenerator::GenerateBehaviorCondition(
conditionCode +=
"for (var i = 0, k = 0, l = " + GetObjectListName(objectName, context) +
".length;i<l;++i) {\n";
conditionCode += " if ( " + predicat + " ) {\n";
conditionCode += " if ( " + predicate + " ) {\n";
conditionCode += " " +
GenerateBooleanFullName(returnBoolean, context) +
" = true;\n";

View File

@@ -319,8 +319,8 @@ class EventsCodeGenerator : public gd::EventsCodeGenerator {
const gd::String& type,
gd::EventsCodeGenerationContext& context);
virtual gd::String GenerateNegatedPredicat(const gd::String& predicat) const {
return "!(" + predicat + ")";
virtual gd::String GenerateNegatedPredicate(const gd::String& predicate) const {
return "!(" + predicate + ")";
};
virtual gd::String GenerateObjectsDeclarationCode(

View File

@@ -133,7 +133,7 @@ CommonInstructionsExtension::CommonInstructionsExtension() {
gd::String conditionsCode = codeGenerator.GenerateConditionsListCode(
event.GetConditions(), context);
gd::String ifPredicat =
gd::String ifPredicate =
event.GetConditions().empty()
? ""
: codeGenerator.GenerateBooleanFullName(
@@ -156,7 +156,7 @@ CommonInstructionsExtension::CommonInstructionsExtension() {
gd::String outputCode;
outputCode += conditionsCode;
if (!ifPredicat.empty()) outputCode += "if (" + ifPredicat + ") ";
if (!ifPredicate.empty()) outputCode += "if (" + ifPredicate + ") ";
outputCode += "{\n";
outputCode += actionsDeclarationsCode;
outputCode += actionsCode;
@@ -391,18 +391,18 @@ CommonInstructionsExtension::CommonInstructionsExtension() {
gd::String whileConditionsStr =
codeGenerator.GenerateConditionsListCode(event.GetWhileConditions(),
context);
gd::String whileIfPredicat = "true";
gd::String whileIfPredicate = "true";
if (!event.GetWhileConditions().empty())
whileIfPredicat =
whileIfPredicate =
codeGenerator.GenerateBooleanFullName("isConditionTrue", context);
gd::String conditionsCode = codeGenerator.GenerateConditionsListCode(
event.GetConditions(), context);
gd::String actionsCode =
codeGenerator.GenerateActionsListCode(event.GetActions(), context);
gd::String ifPredicat = "true";
gd::String ifPredicate = "true";
if (!event.GetConditions().empty())
ifPredicat =
ifPredicate =
codeGenerator.GenerateBooleanFullName("isConditionTrue", context);
// Write final code
@@ -412,9 +412,9 @@ CommonInstructionsExtension::CommonInstructionsExtension() {
outputCode += "do {\n";
outputCode += codeGenerator.GenerateObjectsDeclarationCode(context);
outputCode += whileConditionsStr;
outputCode += "if (" + whileIfPredicat + ") {\n";
outputCode += "if (" + whileIfPredicate + ") {\n";
outputCode += conditionsCode;
outputCode += "if (" + ifPredicat + ") {\n";
outputCode += "if (" + ifPredicate + ") {\n";
outputCode += actionsCode;
outputCode += "\n{ //Subevents: \n";
// TODO: check (and heavily test) if sub events should be generated before
@@ -448,7 +448,7 @@ CommonInstructionsExtension::CommonInstructionsExtension() {
event.GetConditions(), context);
gd::String actionsCode =
codeGenerator.GenerateActionsListCode(event.GetActions(), context);
gd::String ifPredicat = event.GetConditions().empty()
gd::String ifPredicate = event.GetConditions().empty()
? "true"
: codeGenerator.GenerateBooleanFullName(
"isConditionTrue", context);
@@ -524,7 +524,7 @@ CommonInstructionsExtension::CommonInstructionsExtension() {
// Now do the rest of standard event code generation
outputCode += objectDeclaration;
outputCode += conditionsCode;
outputCode += "if (" + ifPredicat + ")\n";
outputCode += "if (" + ifPredicate + ")\n";
outputCode += "{\n";
outputCode += actionsCode;
if (event.HasSubEvents()) {
@@ -613,9 +613,9 @@ CommonInstructionsExtension::CommonInstructionsExtension() {
event.GetConditions(), context);
gd::String actionsCode =
codeGenerator.GenerateActionsListCode(event.GetActions(), context);
gd::String ifPredicat = "true";
gd::String ifPredicate = "true";
if (!event.GetConditions().empty())
ifPredicat =
ifPredicate =
codeGenerator.GenerateBooleanFullName("isConditionTrue", context);
// Prepare object declaration and sub events
@@ -634,7 +634,7 @@ CommonInstructionsExtension::CommonInstructionsExtension() {
" < " + repeatCountVar + ";++" + repeatIndexVar + ") {\n";
outputCode += objectDeclaration;
outputCode += conditionsCode;
outputCode += "if (" + ifPredicat + ")\n";
outputCode += "if (" + ifPredicate + ")\n";
outputCode += "{\n";
outputCode += actionsCode;
if (event.HasSubEvents()) {
@@ -677,9 +677,9 @@ CommonInstructionsExtension::CommonInstructionsExtension() {
event.GetConditions(), context);
gd::String actionsCode =
codeGenerator.GenerateActionsListCode(event.GetActions(), context);
gd::String ifPredicat = "true";
gd::String ifPredicate = "true";
if (!event.GetConditions().empty())
ifPredicat =
ifPredicate =
codeGenerator.GenerateBooleanFullName("isConditionTrue", context);
// Prepare object declaration and sub events
@@ -787,7 +787,7 @@ CommonInstructionsExtension::CommonInstructionsExtension() {
}
outputCode += conditionsCode;
outputCode += "if (" + ifPredicat + ") {\n";
outputCode += "if (" + ifPredicate + ") {\n";
outputCode += actionsCode;
if (event.HasSubEvents()) {
outputCode += "\n{ //Subevents: \n";

View File

@@ -36,6 +36,8 @@ SceneExtension::SceneExtension() {
"gdjs.evtTools.runtimeScene.stopGame");
GetAllConditions()["HasGameJustResumed"].SetFunctionName(
"gdjs.evtTools.runtimeScene.hasGameJustResumed");
GetAllConditions()["DoesSceneExist"].SetFunctionName(
"gdjs.evtTools.runtimeScene.doesSceneExist");
StripUnimplementedInstructionsAndExpressions();
}

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,8 +41,31 @@ namespace gdjs {
if (typeof THREE !== 'undefined') {
this._loader = new THREE_ADDONS.GLTFLoader();
const geometry = new THREE.BoxGeometry(1, 1, 1);
this._invalidModel = new THREE.Mesh(geometry);
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 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,
};
}
}
@@ -77,12 +102,15 @@ namespace gdjs {
let loaded = 0;
for (let i = 0; i < model3DResources.length; ++i) {
const resource = model3DResources[i];
const url = this._resourcesLoader.getFullUrl(resource.file);
this._loader.withCredentials = this._resourcesLoader.checkIfCredentialsRequired(
url
);
this._loader.load(
resource.file,
(gltf) => {
gltf.scene.rotation.order = 'ZYX';
this._loadedThreeModels.set(resource.name, gltf.scene);
url,
(gltf: THREE_ADDONS.GLTF) => {
this._loadedThreeModels.set(resource.name, gltf);
loaded++;
if (loaded === model3DResources.length) {
@@ -114,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());
};
}
}
}

View File

@@ -309,6 +309,16 @@ namespace gdjs {
): boolean => {
return instanceContainer.getGame().hasJustResumed();
};
/**
* Check if a scene exists.
*/
export const doesSceneExist = (
runtimeScene: gdjs.RuntimeScene,
sceneName: string
): boolean => {
return runtimeScene.getGame().hasScene(sceneName);
};
}
}
}

View File

@@ -120,7 +120,7 @@ namespace gdjs {
};
/**
* Convert a Hex number to a RGB color array([r,g,b] with each component going from 0 to 255).
* Convert a Hex number to a RGB color object ({r,g,b,a} with each component going from 0 to 255 and alpha set to 255).
* @param hex Hex color
*/
export const hexNumberToRGB = (
@@ -134,6 +134,20 @@ namespace gdjs {
};
};
/**
* Convert a Hex number to a RGB color array([r,g,b] with each component going from 0 to 255).
* @param hex Hex color
*/
export const hexNumberToRGBArray = (
hexNumber: number
): [integer, integer, integer] => {
return [
(hexNumber >> 16) & 0xff,
(hexNumber >> 8) & 0xff,
hexNumber & 0xff,
];
};
/**
* Get a random integer between 0 and max.
* @param max The maximum value (inclusive).

View File

@@ -223,6 +223,8 @@ namespace gdjs {
* Convert a point from the canvas coordinates (for example,
* the mouse position) to the container coordinates.
*
* This method handles 3D rotations.
*
* @param x The x position, in canvas coordinates.
* @param y The y position, in canvas coordinates.
* @param cameraId The camera number. Currently ignored.
@@ -238,6 +240,11 @@ namespace gdjs {
// The result parameter used to be optional.
let position = result || [0, 0];
if (this._renderer.isCameraRotatedIn3D()) {
return this._renderer.transformTo3DWorld(x, y, 0, cameraId, result);
}
x -= this.getRuntimeScene()._cachedGameResolutionWidth / 2;
y -= this.getRuntimeScene()._cachedGameResolutionHeight / 2;
x /= Math.abs(this._zoomFactor);
@@ -261,6 +268,8 @@ namespace gdjs {
*
* All transformations (scale, rotation) are supported.
*
* This method doesn't handle 3D rotations.
*
* @param x The X position of the point, in parent coordinates.
* @param y The Y position of the point, in parent coordinates.
* @param result Array that will be updated with the result
@@ -295,6 +304,8 @@ namespace gdjs {
* Convert a point from the container coordinates (for example,
* an object position) to the canvas coordinates.
*
* This method doesn't handle 3D rotations.
*
* @param x The x position, in container coordinates.
* @param y The y position, in container coordinates.
* @param cameraId The camera number. Currently ignored.
@@ -333,6 +344,8 @@ namespace gdjs {
*
* All transformations (scale, rotation) are supported.
*
* This method doesn't handle 3D rotations.
*
* @param x The X position of the point, in layer coordinates.
* @param y The Y position of the point, in layer coordinates.
* @param result Array that will be updated with the result

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;
/**
@@ -51,6 +51,8 @@ namespace gdjs {
*/
private static readonly zeroZOrderForPixi = Math.pow(2, -24);
private static vectorForProjections: THREE.Vector3 | null = null;
/**
* @param layer The layer
* @param runtimeInstanceContainerRenderer The scene renderer
@@ -64,7 +66,6 @@ namespace gdjs {
this._pixiContainer.sortableChildren = true;
this._layer = layer;
this._isLightingLayer = layer.isLightingLayer();
this._clearColor = layer.getClearColor();
runtimeInstanceContainerRenderer
.getRendererObject()
.addChild(this._pixiContainer);
@@ -73,11 +74,19 @@ namespace gdjs {
// Setup rendering for lighting or 3D rendering:
const pixiRenderer = runtimeGameRenderer.getPIXIRenderer();
if (this._isLightingLayer) {
this._clearColor = layer.getClearColor();
this._setupLightingRendering(
pixiRenderer,
runtimeInstanceContainerRenderer
);
} else {
// Clear color is used as background color of transparent sprites.
this._clearColor = [
...gdjs.hexNumberToRGBArray(
this._layer.getRuntimeScene().getBackgroundColor()
),
0,
];
this._setup3DRendering(pixiRenderer, runtimeInstanceContainerRenderer);
}
}
@@ -186,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
@@ -212,13 +217,45 @@ 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(
this._threePlaneGeometry,
this._threePlaneMaterial
);
// Force to render the mesh last (after the rest of 3D objects, including
// transparent ones). In most cases, the 2D rendering is composed of a lot
// of transparent areas, and we can't risk it being displayed first and wrongly
// occluding 3D objects shown behind.
this._threePlaneMesh.renderOrder = Number.MAX_SAFE_INTEGER;
this._threeScene.add(this._threePlaneMesh);
}
@@ -337,6 +374,55 @@ namespace gdjs {
}
}
isCameraRotatedIn3D() {
return (
this._threeCamera &&
(this._threeCamera.rotation.x !== 0 ||
this._threeCamera.rotation.y !== 0)
);
}
transformTo3DWorld(
screenX: float,
screenY: float,
worldZ: float,
cameraId: integer,
result: FloatPoint
): FloatPoint {
const camera = this._threeCamera;
if (!camera) {
result[0] = 0;
result[1] = 0;
return result;
}
const width = this._layer.getWidth();
const height = this._layer.getHeight();
let vector = LayerPixiRenderer.vectorForProjections;
if (!vector) {
vector = new THREE.Vector3();
LayerPixiRenderer.vectorForProjections = vector;
}
// https://stackoverflow.com/questions/13055214/mouse-canvas-x-y-to-three-js-world-x-y-z
vector.set((screenX / width) * 2 - 1, -(screenY / height) * 2 + 1, 0.5);
vector.unproject(camera);
vector.sub(camera.position).normalize();
const distance = (worldZ - camera.position.z) / vector.z;
vector.multiplyScalar(distance);
// The plane z == worldZ may not be visible on the camera.
if (!Number.isFinite(vector.x) || !Number.isFinite(vector.y)) {
result[0] = 0;
result[1] = 0;
return result;
}
result[0] = camera.position.x + vector.x;
result[1] = -(camera.position.y + vector.y);
return result;
}
updateVisibility(visible: boolean): void {
this._pixiContainer.visible = !!visible;
if (this._threeGroup) this._threeGroup.visible = !!visible;

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

@@ -134,6 +134,7 @@ namespace gdjs {
(runtimeLayer.isLightingLayer() &&
runtimeLayerRenderer.getLightingSprite()) ||
runtimeLayerRenderer.getRendererObject();
pixiRenderer.render(pixiContainer, { clear: false });
this._layerRenderingMetrics.rendered2DLayersCount++;
@@ -197,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,
@@ -210,6 +215,17 @@ namespace gdjs {
}
}
const debugContainer = this._runtimeScene
.getDebuggerRenderer()
.getRendererObject();
if (debugContainer) {
threeRenderer.resetState();
pixiRenderer.reset();
pixiRenderer.render(debugContainer);
lastRenderWas3D = false;
}
if (!lastRenderWas3D) {
// Out of caution, reset the WebGL states from PixiJS to start again
// with a 3D rendering on the next frame.

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

@@ -280,7 +280,7 @@ namespace gdjs {
* To implement this in your object:
* * Set `gdjs.YourRuntimeObject.supportsReinitialization = true;` to declare support for recycling.
* * Implement `reinitialize`. It **must** call the `reinitialize` of `gdjs.RuntimeObject`, and call `this.onCreated();`
* at the end of `reinitizalize`.
* at the end of `reinitialize`.
* * It must reset the object as if it was newly constructed (be careful about your renderers and any global state).
* * The `_runtimeScene`, `_nameId`, `name` and `type` are guaranteed to stay the same and do not
* need to be set again.

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;

43
GDJS/package-lock.json generated
View File

@@ -14,17 +14,16 @@
"@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",
"minimist": "^1.2.5",
"patch-package": "^6.4.7",
"pixi.js": "^6.1.2",
"pixi.js": "6.1.2",
"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,17 +10,16 @@
"@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",
"minimist": "^1.2.5",
"patch-package": "^6.4.7",
"pixi.js": "^6.1.2",
"pixi.js": "6.1.2",
"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',

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