Compare commits
43
Commits
main
...
internal-merge
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dc61d682c0 | ||
|
|
bf3c45dbb7 | ||
|
|
2591230927 | ||
|
|
d605eaea85 | ||
|
|
4ad7b33bb2 | ||
|
|
91c84cd9d0 | ||
|
|
621413682e | ||
|
|
6502b8027f | ||
|
|
b80c492afb | ||
|
|
ca0853615b | ||
|
|
0530bd0732 | ||
|
|
294b9f302a | ||
|
|
d7c9f2595a | ||
|
|
2ec0500cd5 | ||
|
|
959aed576c | ||
|
|
6272c3e98f | ||
|
|
5ec57c5c39 | ||
|
|
a342783de4 | ||
|
|
eac0dac9b2 | ||
|
|
d6488d37ad | ||
|
|
995a28e88a | ||
|
|
6dd4058ac6 | ||
|
|
b447031e2d | ||
|
|
17bea45cd6 | ||
|
|
8fc068e718 | ||
|
|
cf2c3689c5 | ||
|
|
491e5df430 | ||
|
|
0d9bf82b06 | ||
|
|
54eea79dec | ||
|
|
4aaa3ba06b | ||
|
|
606e98c192 | ||
|
|
d8964db5d9 | ||
|
|
13e6b9a56f | ||
|
|
2e7a8dcd21 | ||
|
|
df386d7e68 | ||
|
|
5671b87e05 | ||
|
|
d922e6112f | ||
|
|
6fb716be07 | ||
|
|
b6d3333dfb | ||
|
|
c3b1900154 | ||
|
|
c20c29250d | ||
|
|
143cc447c8 | ||
|
|
5a176e0ccc |
+78
-80
@@ -11,22 +11,21 @@ jobs:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Pulling the new commit
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setting up Haxe
|
||||
uses: krdlab/setup-haxe@v2
|
||||
with:
|
||||
haxe-version: 4.3.7
|
||||
- name: Restore existing build cache for faster compilation
|
||||
uses: actions/cache@v4.2.3
|
||||
with:
|
||||
# not caching the bin folder to prevent asset duplication and stuff like that
|
||||
key: cache-build-linux
|
||||
path: |
|
||||
.haxelib/
|
||||
export/release/linux/haxe/
|
||||
export/release/linux/obj/
|
||||
# - name: Restore existing build cache for faster compilation
|
||||
# uses: actions/cache@v4.2.3
|
||||
# with:
|
||||
# # not caching the bin folder to prevent asset duplication and stuff like that
|
||||
# key: cache-build-linux
|
||||
# path: |
|
||||
# .haxelib/
|
||||
# export/release/linux/obj/
|
||||
- run: |
|
||||
echo "HXCPP_COMPILE_CACHE=~/.hxcpp" >> $GITHUB_ENV
|
||||
- name: Installing LibVLC
|
||||
@@ -37,47 +36,47 @@ jobs:
|
||||
haxe -cp commandline -D analyzer-optimize --run Main setup -s
|
||||
- name: Building the game
|
||||
run: |
|
||||
haxelib run lime rebuild linux -nocolor -nocffi -release
|
||||
haxelib run lime build linux -DCOMPILE_EXPERIMENTAL
|
||||
# - name: Tar files
|
||||
# run: tar -zcvf CodenameEngine.tar.gz -C export/release/linux/bin .
|
||||
- name: Uploading artifact (executable)
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: Codename Engine (Executable Only)
|
||||
path: export/release/linux/bin/CodenameEngine
|
||||
- name: Uploading artifact (entire build)
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: Codename Engine
|
||||
path: export/release/linux/bin/
|
||||
- name: Clearing already existing cache
|
||||
uses: actions/github-script@v6
|
||||
with:
|
||||
script: |
|
||||
const caches = await github.rest.actions.getActionsCacheList({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
})
|
||||
for (const cache of caches.data.actions_caches) {
|
||||
if (cache.key == "cache-build-linux") {
|
||||
console.log('Clearing ' + cache.key + '...')
|
||||
await github.rest.actions.deleteActionsCacheById({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
cache_id: cache.id,
|
||||
})
|
||||
console.log("Cache cleared.")
|
||||
}
|
||||
}
|
||||
- name: Uploading new cache
|
||||
uses: actions/cache@v4.2.3
|
||||
with:
|
||||
# caching again since for some reason it doesnt work with the first post cache shit
|
||||
key: cache-build-linux
|
||||
path: |
|
||||
.haxelib/
|
||||
export/release/linux/haxe/
|
||||
export/release/linux/obj/
|
||||
# - name: Clearing already existing cache
|
||||
# uses: actions/github-script@v6
|
||||
# with:
|
||||
# script: |
|
||||
# const caches = await github.rest.actions.getActionsCacheList({
|
||||
# owner: context.repo.owner,
|
||||
# repo: context.repo.repo,
|
||||
# })
|
||||
# for (const cache of caches.data.actions_caches) {
|
||||
# if (cache.key == "cache-build-linux") {
|
||||
# console.log('Clearing ' + cache.key + '...')
|
||||
# await github.rest.actions.deleteActionsCacheById({
|
||||
# owner: context.repo.owner,
|
||||
# repo: context.repo.repo,
|
||||
# cache_id: cache.id,
|
||||
# })
|
||||
# console.log("Cache cleared.")
|
||||
# }
|
||||
# }
|
||||
# - name: Uploading new cache
|
||||
# uses: actions/cache@v4.2.3
|
||||
# with:
|
||||
# # caching again since for some reason it doesnt work with the first post cache shit
|
||||
# key: cache-build-linux
|
||||
# path: |
|
||||
# .haxelib/
|
||||
# export/release/linux/obj/
|
||||
|
||||
# didnt compile debug in the same job or github would have said that job wasn't completed until debug was done too (debug uploads are not essential)
|
||||
debug_build:
|
||||
@@ -87,20 +86,19 @@ jobs:
|
||||
needs: build # since its low priority, it'll run after, so actions will concentrate first on normal builds
|
||||
steps:
|
||||
- name: Pulling the new commit
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
- name: Setting up Haxe
|
||||
uses: krdlab/setup-haxe@v2
|
||||
with:
|
||||
haxe-version: 4.3.7
|
||||
- name: Restore existing build cache for faster compilation
|
||||
uses: actions/cache@v4.2.3
|
||||
with:
|
||||
# not caching the bin folder to prevent asset duplication and stuff like that
|
||||
key: cache-build-linux-debug
|
||||
path: |
|
||||
.haxelib/
|
||||
export/debug/linux/haxe/
|
||||
export/debug/linux/obj/
|
||||
# - name: Restore existing build cache for faster compilation
|
||||
# uses: actions/cache@v4.2.3
|
||||
# with:
|
||||
# # not caching the bin folder to prevent asset duplication and stuff like that
|
||||
# key: cache-build-linux-debug
|
||||
# path: |
|
||||
# .haxelib/
|
||||
# export/debug/linux/obj/
|
||||
- run: |
|
||||
echo "HXCPP_COMPILE_CACHE=~/.hxcpp" >> $GITHUB_ENV
|
||||
- name: Installing LibVLC
|
||||
@@ -111,39 +109,39 @@ jobs:
|
||||
haxe -cp commandline -D analyzer-optimize --run Main setup -s
|
||||
- name: Building the game
|
||||
run: |
|
||||
haxelib run lime build linux -debug
|
||||
haxelib run lime rebuild linux -nocolor -nocffi -debug
|
||||
haxelib run lime build linux -debug -DCOMPILE_EXPERIMENTAL
|
||||
# - name: Tar files
|
||||
# run: tar -zcvf CodenameEngine.tar.gz -C export/debug/linux/bin .
|
||||
- name: Uploading artifact (entire build)
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: Codename Engine Debug
|
||||
path: export/debug/linux/bin/
|
||||
- name: Clearing already existing cache
|
||||
uses: actions/github-script@v6
|
||||
with:
|
||||
script: |
|
||||
const caches = await github.rest.actions.getActionsCacheList({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
})
|
||||
for (const cache of caches.data.actions_caches) {
|
||||
if (cache.key == "cache-build-linux-debug") {
|
||||
console.log('Clearing ' + cache.key + '...')
|
||||
await github.rest.actions.deleteActionsCacheById({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
cache_id: cache.id,
|
||||
})
|
||||
console.log("Cache cleared.")
|
||||
}
|
||||
}
|
||||
- name: Uploading new cache
|
||||
uses: actions/cache@v4.2.3
|
||||
with:
|
||||
# caching again since for some reason it doesnt work with the first post cache shit
|
||||
key: cache-build-linux-debug
|
||||
path: |
|
||||
.haxelib/
|
||||
export/debug/linux/haxe/
|
||||
export/debug/linux/obj/
|
||||
# - name: Clearing already existing cache
|
||||
# uses: actions/github-script@v6
|
||||
# with:
|
||||
# script: |
|
||||
# const caches = await github.rest.actions.getActionsCacheList({
|
||||
# owner: context.repo.owner,
|
||||
# repo: context.repo.repo,
|
||||
# })
|
||||
# for (const cache of caches.data.actions_caches) {
|
||||
# if (cache.key == "cache-build-linux-debug") {
|
||||
# console.log('Clearing ' + cache.key + '...')
|
||||
# await github.rest.actions.deleteActionsCacheById({
|
||||
# owner: context.repo.owner,
|
||||
# repo: context.repo.repo,
|
||||
# cache_id: cache.id,
|
||||
# })
|
||||
# console.log("Cache cleared.")
|
||||
# }
|
||||
# }
|
||||
# - name: Uploading new cache
|
||||
# uses: actions/cache@v4.2.3
|
||||
# with:
|
||||
# # caching again since for some reason it doesnt work with the first post cache shit
|
||||
# key: cache-build-linux-debug
|
||||
# path: |
|
||||
# .haxelib/
|
||||
# export/debug/linux/obj/
|
||||
|
||||
+81
-83
@@ -8,25 +8,24 @@ jobs:
|
||||
build:
|
||||
name: Mac OS Build
|
||||
permissions: write-all
|
||||
runs-on: macos-14
|
||||
runs-on: macos-26
|
||||
steps:
|
||||
- name: Pulling the new commit
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setting up Haxe
|
||||
uses: krdlab/setup-haxe@v2
|
||||
with:
|
||||
haxe-version: 4.3.7
|
||||
- name: Restore existing build cache for faster compilation
|
||||
uses: actions/cache@v4.2.3
|
||||
with:
|
||||
# not caching the bin folder to prevent asset duplication and stuff like that
|
||||
key: cache-build-mac
|
||||
path: |
|
||||
.haxelib/
|
||||
export/release/macos/haxe/
|
||||
export/release/macos/obj/
|
||||
# - name: Restore existing build cache for faster compilation
|
||||
# uses: actions/cache@v4.2.3
|
||||
# with:
|
||||
# # not caching the bin folder to prevent asset duplication and stuff like that
|
||||
# key: cache-build-mac
|
||||
# path: |
|
||||
# .haxelib/
|
||||
# export/release/macos/obj/
|
||||
- run: |
|
||||
echo "HXCPP_COMPILE_CACHE=~/.hxcpp" >> $GITHUB_ENV
|
||||
- name: Installing/Updating libraries
|
||||
@@ -34,70 +33,69 @@ jobs:
|
||||
haxe -cp commandline -D analyzer-optimize --run Main setup -s
|
||||
- name: Building the game
|
||||
run: |
|
||||
arch -x86_64 haxelib run lime build mac -DCOMPILE_EXPERIMENTAL
|
||||
haxelib run lime rebuild mac -nocolor -nocffi -release
|
||||
haxelib run lime build mac -DCOMPILE_EXPERIMENTAL
|
||||
- name: Tar files
|
||||
run: tar -zcvf CodenameEngine.tar.gz -C export/release/macos/bin .
|
||||
- name: Uploading artifact (executable)
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: Codename Engine (Executable Only)
|
||||
path: export/release/macos/bin/CodenameEngine.app/Contents/MacOS/CodenameEngine
|
||||
- name: Uploading artifact (entire build)
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: Codename Engine
|
||||
path: CodenameEngine.tar.gz
|
||||
- name: Clearing already existing cache
|
||||
uses: actions/github-script@v6
|
||||
with:
|
||||
script: |
|
||||
const caches = await github.rest.actions.getActionsCacheList({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
})
|
||||
for (const cache of caches.data.actions_caches) {
|
||||
if (cache.key == "cache-build-mac") {
|
||||
console.log('Clearing ' + cache.key + '...')
|
||||
await github.rest.actions.deleteActionsCacheById({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
cache_id: cache.id,
|
||||
})
|
||||
console.log("Cache cleared.")
|
||||
}
|
||||
}
|
||||
- name: Uploading new cache
|
||||
uses: actions/cache@v4.2.3
|
||||
with:
|
||||
# caching again since for some reason it doesnt work with the first post cache shit
|
||||
key: cache-build-mac
|
||||
path: |
|
||||
.haxelib/
|
||||
export/release/macos/haxe/
|
||||
export/release/macos/obj/
|
||||
# - name: Clearing already existing cache
|
||||
# uses: actions/github-script@v6
|
||||
# with:
|
||||
# script: |
|
||||
# const caches = await github.rest.actions.getActionsCacheList({
|
||||
# owner: context.repo.owner,
|
||||
# repo: context.repo.repo,
|
||||
# })
|
||||
# for (const cache of caches.data.actions_caches) {
|
||||
# if (cache.key == "cache-build-mac") {
|
||||
# console.log('Clearing ' + cache.key + '...')
|
||||
# await github.rest.actions.deleteActionsCacheById({
|
||||
# owner: context.repo.owner,
|
||||
# repo: context.repo.repo,
|
||||
# cache_id: cache.id,
|
||||
# })
|
||||
# console.log("Cache cleared.")
|
||||
# }
|
||||
# }
|
||||
# - name: Uploading new cache
|
||||
# uses: actions/cache@v4.2.3
|
||||
# with:
|
||||
# # caching again since for some reason it doesnt work with the first post cache shit
|
||||
# key: cache-build-mac
|
||||
# path: |
|
||||
# .haxelib/
|
||||
# export/release/macos/obj/
|
||||
|
||||
# didnt compile debug in the same job or github would have said that job wasn't completed until debug was done too (debug uploads are not essential)
|
||||
debug_build:
|
||||
name: Mac OS Debug Build
|
||||
permissions: write-all
|
||||
runs-on: macos-14
|
||||
runs-on: macos-26
|
||||
needs: build # since its low priority, it'll run after, so actions will concentrate first on normal builds
|
||||
steps:
|
||||
- name: Pulling the new commit
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
- name: Setting up Haxe
|
||||
uses: krdlab/setup-haxe@v2
|
||||
with:
|
||||
haxe-version: 4.3.7
|
||||
- name: Restore existing build cache for faster compilation
|
||||
uses: actions/cache@v4.2.3
|
||||
with:
|
||||
# not caching the bin folder to prevent asset duplication and stuff like that
|
||||
key: cache-build-mac-debug
|
||||
path: |
|
||||
.haxelib/
|
||||
export/debug/macos/haxe/
|
||||
export/debug/macos/obj/
|
||||
# - name: Restore existing build cache for faster compilation
|
||||
# uses: actions/cache@v4.2.3
|
||||
# with:
|
||||
# # not caching the bin folder to prevent asset duplication and stuff like that
|
||||
# key: cache-build-mac-debug
|
||||
# path: |
|
||||
# .haxelib/
|
||||
# export/debug/macos/obj/
|
||||
- run: |
|
||||
echo "HXCPP_COMPILE_CACHE=~/.hxcpp" >> $GITHUB_ENV
|
||||
- name: Installing/Updating libraries
|
||||
@@ -105,39 +103,39 @@ jobs:
|
||||
haxe -cp commandline -D analyzer-optimize --run Main setup -s
|
||||
- name: Building the game
|
||||
run: |
|
||||
arch -x86_64 haxelib run lime build mac -debug
|
||||
haxelib run lime rebuild mac -nocolor -nocffi -debug
|
||||
haxelib run lime build mac -debug -DCOMPILE_EXPERIMENTAL
|
||||
- name: Tar files
|
||||
run: tar -zcvf CodenameEngine.tar.gz -C export/debug/macos/bin .
|
||||
- name: Uploading artifact (entire build)
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: Codename Engine Debug
|
||||
path: CodenameEngine.tar.gz
|
||||
- name: Clearing already existing cache
|
||||
uses: actions/github-script@v6
|
||||
with:
|
||||
script: |
|
||||
const caches = await github.rest.actions.getActionsCacheList({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
})
|
||||
for (const cache of caches.data.actions_caches) {
|
||||
if (cache.key == "cache-build-mac-debug") {
|
||||
console.log('Clearing ' + cache.key + '...')
|
||||
await github.rest.actions.deleteActionsCacheById({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
cache_id: cache.id,
|
||||
})
|
||||
console.log("Cache cleared.")
|
||||
}
|
||||
}
|
||||
- name: Uploading new cache
|
||||
uses: actions/cache@v4.2.3
|
||||
with:
|
||||
# caching again since for some reason it doesnt work with the first post cache shit
|
||||
key: cache-build-mac-debug
|
||||
path: |
|
||||
.haxelib/
|
||||
export/debug/macos/haxe/
|
||||
export/debug/macos/obj/
|
||||
# - name: Clearing already existing cache
|
||||
# uses: actions/github-script@v6
|
||||
# with:
|
||||
# script: |
|
||||
# const caches = await github.rest.actions.getActionsCacheList({
|
||||
# owner: context.repo.owner,
|
||||
# repo: context.repo.repo,
|
||||
# })
|
||||
# for (const cache of caches.data.actions_caches) {
|
||||
# if (cache.key == "cache-build-mac-debug") {
|
||||
# console.log('Clearing ' + cache.key + '...')
|
||||
# await github.rest.actions.deleteActionsCacheById({
|
||||
# owner: context.repo.owner,
|
||||
# repo: context.repo.repo,
|
||||
# cache_id: cache.id,
|
||||
# })
|
||||
# console.log("Cache cleared.")
|
||||
# }
|
||||
# }
|
||||
# - name: Uploading new cache
|
||||
# uses: actions/cache@v4.2.3
|
||||
# with:
|
||||
# # caching again since for some reason it doesnt work with the first post cache shit
|
||||
# key: cache-build-mac-debug
|
||||
# path: |
|
||||
# .haxelib/
|
||||
# export/debug/macos/obj/
|
||||
|
||||
@@ -21,12 +21,12 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Download Windows Full Build
|
||||
uses: dawidd6/action-download-artifact@v6
|
||||
uses: dawidd6/action-download-artifact@v8
|
||||
with:
|
||||
workflow: windows.yml
|
||||
name: Codename Engine
|
||||
@@ -34,7 +34,7 @@ jobs:
|
||||
allow_forks: false
|
||||
|
||||
- name: Download Windows Executable
|
||||
uses: dawidd6/action-download-artifact@v6
|
||||
uses: dawidd6/action-download-artifact@v8
|
||||
with:
|
||||
workflow: windows.yml
|
||||
name: Codename Engine (Executable Only)
|
||||
@@ -42,7 +42,7 @@ jobs:
|
||||
allow_forks: false
|
||||
|
||||
- name: Download Mac OS Full Build
|
||||
uses: dawidd6/action-download-artifact@v6
|
||||
uses: dawidd6/action-download-artifact@v8
|
||||
with:
|
||||
workflow: macos.yml
|
||||
name: Codename Engine
|
||||
@@ -50,7 +50,7 @@ jobs:
|
||||
allow_forks: false
|
||||
|
||||
- name: Download Mac OS Executable
|
||||
uses: dawidd6/action-download-artifact@v6
|
||||
uses: dawidd6/action-download-artifact@v8
|
||||
with:
|
||||
workflow: macos.yml
|
||||
name: Codename Engine (Executable Only)
|
||||
@@ -58,7 +58,7 @@ jobs:
|
||||
allow_forks: false
|
||||
|
||||
- name: Download Linux Full Build
|
||||
uses: dawidd6/action-download-artifact@v6
|
||||
uses: dawidd6/action-download-artifact@v8
|
||||
with:
|
||||
workflow: linux.yml
|
||||
name: Codename Engine
|
||||
@@ -66,7 +66,7 @@ jobs:
|
||||
allow_forks: false
|
||||
|
||||
- name: Download Linux Executable
|
||||
uses: dawidd6/action-download-artifact@v6
|
||||
uses: dawidd6/action-download-artifact@v8
|
||||
with:
|
||||
workflow: linux.yml
|
||||
name: Codename Engine (Executable Only)
|
||||
|
||||
@@ -11,22 +11,21 @@ jobs:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- name: Pulling the new commit
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setting up Haxe
|
||||
uses: krdlab/setup-haxe@v2
|
||||
with:
|
||||
haxe-version: 4.3.7
|
||||
- name: Restore existing build cache for faster compilation
|
||||
uses: actions/cache@v4.2.3
|
||||
with:
|
||||
# not caching the bin folder to prevent asset duplication and stuff like that
|
||||
key: cache-build-windows
|
||||
path: |
|
||||
.haxelib/
|
||||
export/release/windows/haxe/
|
||||
export/release/windows/obj/
|
||||
# - name: Restore existing build cache for faster compilation
|
||||
# uses: actions/cache@v4.2.3
|
||||
# with:
|
||||
# # not caching the bin folder to prevent asset duplication and stuff like that
|
||||
# key: cache-build-windows
|
||||
# path: |
|
||||
# .haxelib/
|
||||
# export/release/windows/obj/
|
||||
- run: |
|
||||
echo "HXCPP_COMPILE_CACHE=~/.hxcpp" >> $GITHUB_ENV
|
||||
- name: Installing/Updating libraries
|
||||
@@ -34,45 +33,47 @@ jobs:
|
||||
haxe -cp commandline -D analyzer-optimize --run Main setup -s --no-vscheck
|
||||
- name: Building the game
|
||||
run: |
|
||||
haxelib run lime rebuild windows -nocolor -nocffi -release
|
||||
haxelib run lime build windows -DCOMPILE_EXPERIMENTAL
|
||||
- name: Uploading artifact (executable)
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: Codename Engine (Executable Only)
|
||||
path: export/release/windows/bin/CodenameEngine.exe
|
||||
path: |
|
||||
export/release/windows/bin/CodenameEngine.exe
|
||||
export/release/windows/bin/lime.ndll
|
||||
- name: Uploading artifact (entire build)
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: Codename Engine
|
||||
path: export/release/windows/bin
|
||||
- name: Clearing already existing cache
|
||||
uses: actions/github-script@v6
|
||||
with:
|
||||
script: |
|
||||
const caches = await github.rest.actions.getActionsCacheList({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
})
|
||||
for (const cache of caches.data.actions_caches) {
|
||||
if (cache.key == "cache-build-windows") {
|
||||
console.log('Clearing ' + cache.key + '...')
|
||||
await github.rest.actions.deleteActionsCacheById({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
cache_id: cache.id,
|
||||
})
|
||||
console.log("Cache cleared.")
|
||||
}
|
||||
}
|
||||
- name: Uploading new cache
|
||||
uses: actions/cache@v4.2.3
|
||||
with:
|
||||
# caching again since for some reason it doesnt work with the first post cache shit
|
||||
key: cache-build-windows
|
||||
path: |
|
||||
.haxelib/
|
||||
export/release/windows/haxe/
|
||||
export/release/windows/obj/
|
||||
# - name: Clearing already existing cache
|
||||
# uses: actions/github-script@v6
|
||||
# with:
|
||||
# script: |
|
||||
# const caches = await github.rest.actions.getActionsCacheList({
|
||||
# owner: context.repo.owner,
|
||||
# repo: context.repo.repo,
|
||||
# })
|
||||
# for (const cache of caches.data.actions_caches) {
|
||||
# if (cache.key == "cache-build-windows") {
|
||||
# console.log('Clearing ' + cache.key + '...')
|
||||
# await github.rest.actions.deleteActionsCacheById({
|
||||
# owner: context.repo.owner,
|
||||
# repo: context.repo.repo,
|
||||
# cache_id: cache.id,
|
||||
# })
|
||||
# console.log("Cache cleared.")
|
||||
# }
|
||||
# }
|
||||
# - name: Uploading new cache
|
||||
# uses: actions/cache@v4.2.3
|
||||
# with:
|
||||
# # caching again since for some reason it doesnt work with the first post cache shit
|
||||
# key: cache-build-windows
|
||||
# path: |
|
||||
# .haxelib/
|
||||
# export/release/windows/obj/
|
||||
|
||||
# didnt compile debug in the same job or github would have said that job wasn't completed until debug was done too (debug uploads are not essential)
|
||||
debug_build:
|
||||
@@ -82,20 +83,20 @@ jobs:
|
||||
needs: build # since its low priority, it'll run after, so actions will concentrate first on normal builds
|
||||
steps:
|
||||
- name: Pulling the new commit
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
- name: Setting up Haxe
|
||||
uses: krdlab/setup-haxe@v2
|
||||
with:
|
||||
haxe-version: 4.3.7
|
||||
- name: Restore existing build cache for faster compilation
|
||||
uses: actions/cache@v4.2.3
|
||||
with:
|
||||
# not caching the bin folder to prevent asset duplication and stuff like that
|
||||
key: cache-build-windows-debug
|
||||
path: |
|
||||
.haxelib/
|
||||
export/debug/windows/haxe/
|
||||
export/debug/windows/obj/
|
||||
# - name: Restore existing build cache for faster compilation
|
||||
# uses: actions/cache@v4.2.3
|
||||
# with:
|
||||
# # not caching the bin folder to prevent asset duplication and stuff like that
|
||||
# key: cache-build-windows-debug
|
||||
# path: |
|
||||
# .haxelib/
|
||||
# export/debug/windows/haxe/
|
||||
# export/debug/windows/obj/
|
||||
- run: |
|
||||
echo "HXCPP_COMPILE_CACHE=~/.hxcpp" >> $GITHUB_ENV
|
||||
- name: Installing/Updating libraries
|
||||
@@ -103,37 +104,37 @@ jobs:
|
||||
haxe -cp commandline -D analyzer-optimize --run Main setup -s --no-vscheck
|
||||
- name: Building the game
|
||||
run: |
|
||||
haxelib run lime rebuild windows -nocolor -nocffi -debug
|
||||
haxelib run lime build windows -debug
|
||||
- name: Uploading artifact (entire build)
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: Codename Engine Debug
|
||||
path: export/debug/windows/bin
|
||||
- name: Clearing already existing cache
|
||||
uses: actions/github-script@v6
|
||||
with:
|
||||
script: |
|
||||
const caches = await github.rest.actions.getActionsCacheList({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
})
|
||||
for (const cache of caches.data.actions_caches) {
|
||||
if (cache.key == "cache-build-windows-debug") {
|
||||
console.log('Clearing ' + cache.key + '...')
|
||||
await github.rest.actions.deleteActionsCacheById({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
cache_id: cache.id,
|
||||
})
|
||||
console.log("Cache cleared.")
|
||||
}
|
||||
}
|
||||
- name: Uploading new cache
|
||||
uses: actions/cache@v4.2.3
|
||||
with:
|
||||
# caching again since for some reason it doesnt work with the first post cache shit
|
||||
key: cache-build-windows-debug
|
||||
path: |
|
||||
.haxelib/
|
||||
export/debug/windows/haxe/
|
||||
export/debug/windows/obj/
|
||||
# - name: Clearing already existing cache
|
||||
# uses: actions/github-script@v6
|
||||
# with:
|
||||
# script: |
|
||||
# const caches = await github.rest.actions.getActionsCacheList({
|
||||
# owner: context.repo.owner,
|
||||
# repo: context.repo.repo,
|
||||
# })
|
||||
# for (const cache of caches.data.actions_caches) {
|
||||
# if (cache.key == "cache-build-windows-debug") {
|
||||
# console.log('Clearing ' + cache.key + '...')
|
||||
# await github.rest.actions.deleteActionsCacheById({
|
||||
# owner: context.repo.owner,
|
||||
# repo: context.repo.repo,
|
||||
# cache_id: cache.id,
|
||||
# })
|
||||
# console.log("Cache cleared.")
|
||||
# }
|
||||
# }
|
||||
# - name: Uploading new cache
|
||||
# uses: actions/cache@v4.2.3
|
||||
# with:
|
||||
# # caching again since for some reason it doesnt work with the first post cache shit
|
||||
# key: cache-build-windows-debug
|
||||
# path: |
|
||||
# .haxelib/
|
||||
# export/debug/windows/obj/
|
||||
|
||||
@@ -47,7 +47,7 @@ The engine uses [HaxeFlixel](https://haxeflixel.com/) and it mainly features:
|
||||
---
|
||||
|
||||
> [!NOTE]
|
||||
> Codename Engine as for now supports **Windows x64**, **Mac OS x64** and **Linux x64**.<br>
|
||||
> Codename Engine as for now supports **Windows x64**, **Mac OS Universal** and **Linux x64**.<br>
|
||||
> More platforms will soon come, stay tuned!<br>
|
||||
> - [ ] **Web (HTML5) Support**
|
||||
> - [ ] **Mobile Support**
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#pragma header
|
||||
|
||||
// Used in charter by waveforms
|
||||
#version 120 // this shader doesnt require expensive calculations
|
||||
|
||||
const vec3 gradient1 = vec3(114.0/255.0, 81.0/255.0, 135.0/255.0);
|
||||
const vec3 gradient2 = vec3(144.0/255.0, 80.0/255.0, 186.0/255.0);
|
||||
@@ -18,6 +17,21 @@ uniform vec2 waveformSize;
|
||||
|
||||
uniform bool lowDetail;
|
||||
|
||||
float catmullRom(
|
||||
float p0, float p1, float p2, float p3,
|
||||
float t
|
||||
) {
|
||||
float t2 = t * t;
|
||||
float t3 = t2 * t;
|
||||
|
||||
return 0.5 * (
|
||||
(2.0 * p1) +
|
||||
(-p0 + p2) * t +
|
||||
(2.0*p0 - 5.0*p1 + 4.0*p2 - p3) * t2 +
|
||||
(-p0 + 3.0*p1 - 3.0*p2 + p3) * t3
|
||||
);
|
||||
}
|
||||
|
||||
float getAmplitude(vec2 pixel) {
|
||||
float pixelID = floor((pixel.y+pixelOffset)/3.0);
|
||||
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
[General]
|
||||
sample-type=float32
|
||||
stereo-mode=speakers
|
||||
hrtf=false
|
||||
cf_level=0
|
||||
output-limiter=false
|
||||
front-stabilizer=false
|
||||
volume-adjust=0
|
||||
period_size=441
|
||||
sources=512
|
||||
sends=64
|
||||
dither=false
|
||||
|
||||
[decoder]
|
||||
hq-mode=true
|
||||
distance-comp=true
|
||||
nfc=false
|
||||
+26
-28
@@ -3,35 +3,32 @@
|
||||
<if cond="update || all">
|
||||
<print pretty="true">Preparing installation...</print>
|
||||
|
||||
<!-- OpenFL & Lime (Required for Flixel) -->
|
||||
<git name="openfl" url="https://github.com/CodenameCrew/cne-openfl" ref="old" />
|
||||
<lib name="lime" version="8.1.2" />
|
||||
<!-- Hxcpp & OpenFL & Lime (Required for Flixel) -->
|
||||
<git name="hxcpp" url="https://github.com/CodenameCrew/cne-hxcpp"/>
|
||||
<git name="openfl" url="https://github.com/CodenameCrew/cne-openfl" ref="cne" skipDeps="true"/>
|
||||
<git name="lime" url="https://github.com/CodenameCrew/cne-lime" ref="cne" skipDeps="true"/>
|
||||
|
||||
<!-- Flixel -->
|
||||
<git name="flixel" url="https://github.com/CodenameCrew/cne-flixel" ref="old" />
|
||||
<git name="flixel-addons" url="https://github.com/CodenameCrew/cne-flixel-addons" ref="old" />
|
||||
<git name="flixel" url="https://github.com/CodenameCrew/cne-flixel" ref="cne" skipDeps="true"/>
|
||||
<git name="flixel-addons" url="https://github.com/CodenameCrew/cne-flixel-addons" ref="dev" skipDeps="true"/>
|
||||
|
||||
<!-- CNE Core Libraries -->
|
||||
<git name="flixel-animate" url="https://github.com/CodenameCrew/cne-flixel-animate" skipDeps="true"/>
|
||||
<git name="hscript-improved" url="https://github.com/CodenameCrew/hscript-improved" ref="codename-dev"/>
|
||||
<git name="away3d" url="https://github.com/CodenameCrew/away3d" skipDeps="true"/>
|
||||
|
||||
<!-- Other Libraries -->
|
||||
<git name="hscript-improved" url="https://github.com/CodenameCrew/hscript-improved" ref="codename-dev" />
|
||||
<git name="hxdiscord_rpc" url="https://github.com/CodenameCrew/cne-hxdiscord_rpc" ref="old" skipDeps="true" />
|
||||
<lib name="funkin-modchart" skipDeps="true" />
|
||||
<lib name="hxvlc" version="1.9.3" skipDeps="true" />
|
||||
|
||||
<!-- Documentation and other features -->
|
||||
<git name="away3d" url="https://github.com/CodenameCrew/away3d" />
|
||||
<!-- <lib name="dox" /> -->
|
||||
<lib name="markdown" />
|
||||
<lib name="hxcpp-debug-server" />
|
||||
|
||||
<lib name="nape-haxe4" />
|
||||
<git name="hxdiscord_rpc" url="https://github.com/CodenameCrew/cne-hxdiscord_rpc" skipDeps="true"/>
|
||||
<git name="hxvlc" url="https://github.com/CodenameCrew/cne-hxvlc" skipDeps="true"/>
|
||||
<lib name="funkin-modchart" skipDeps="true"/>
|
||||
<lib name="hxcpp-debug-server"/>
|
||||
<lib name="format"/>
|
||||
<lib name="markdown"/>
|
||||
<lib name="nape-haxe4"/>
|
||||
|
||||
<!-- For building lime -->
|
||||
<lib global="true" name="hxp" /> <lib name="hxp" />
|
||||
<lib global="true" name="format" /> <lib name="format" />
|
||||
|
||||
<git name="hxcpp" url="https://github.com/CodenameCrew/cne-hxcpp" ref="old" />
|
||||
|
||||
<git name="flixel-animate" url="https://github.com/CodenameCrew/cne-flixel-animate/" ref="old" skipDeps="true"/>
|
||||
<!-- <lib global="true" name="hxp"/> <lib name="hxp"/> -->
|
||||
<!-- <lib global="true" name="format"/> <lib name="format"/> -->
|
||||
</if>
|
||||
|
||||
<!-- Hxcpp Building -->
|
||||
@@ -40,15 +37,16 @@
|
||||
|
||||
<!-- Build hxcpp -->
|
||||
<cmd inLib="hxcpp" dir="tools/hxcpp" cmd="haxe compile.hxml" />
|
||||
<cmd inLib="lime" cmd="haxelib run lime rebuild hxcpp"/>
|
||||
</if>
|
||||
|
||||
<!-- Lime Building -->
|
||||
<!-- <if cond="build-lime || all">
|
||||
<if cond="build-lime">
|
||||
<print pretty="true">Rebuilding Lime...</print>
|
||||
|
||||
<!- - Build lime - ->
|
||||
<cmd inLib="lime" cmd="haxelib run lime rebuild . $PLATFORM -release -clean" />
|
||||
<cmd inLib="lime" dir="tools" cmd="haxe tools.hxml" />
|
||||
</if> -->
|
||||
<!-- Build lime -->
|
||||
<cmd inLib="lime" dir="tools" cmd="haxe tools.hxml"/>
|
||||
<cmd inLib="lime" cmd="haxelib run lime rebuild $PLATFORM -nocolor -nocffi -release"/>
|
||||
</if>
|
||||
|
||||
</libraries>
|
||||
|
||||
+144
-148
@@ -1,84 +1,54 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<project>
|
||||
<!-- _________________________ Application Settings _________________________ -->
|
||||
<app
|
||||
title="Friday Night Funkin' - Codename Engine" main="funkin.backend.system.Main"
|
||||
file="CodenameEngine" packageName="com.yoshman29.codenameengine"
|
||||
package="com.yoshman29.codenameengine"
|
||||
version="1.0.1" company="Yoshman29" />
|
||||
<project xmlns="http://lime.software/project/1.0.4" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://lime.software/project/1.0.4 http://lime.software/xsd/project-1.0.4.xsd">
|
||||
<!-- ____________________________ Window Settings ___________________________ -->
|
||||
|
||||
<!--
|
||||
CHANGE THE SAVE PATH & NAME FOR YOUR MOD HERE!
|
||||
-->
|
||||
<!-- CHANGE THE SAVE PATH & NAME FOR YOUR MOD HERE! -->
|
||||
<haxedef name="SAVE_PATH" value="CodenameEngine"/>
|
||||
<haxedef name="SAVE_NAME" value="save-default"/>
|
||||
|
||||
<haxedef name="SAVE_OPTIONS_PATH" value="CodenameEngine"/>
|
||||
<haxedef name="SAVE_OPTIONS_NAME" value="options"/>
|
||||
|
||||
<!--Switch Export with Unique ApplicationID and Icon-->
|
||||
<set name="APP_ID" value="0x0100f6c013bbc000" />
|
||||
<!-- Comment this out to disable commit number on FPS -->
|
||||
<define name="SHOW_BUILD_ON_FPS"/>
|
||||
|
||||
<!-- DO NOT REMOVE!! ALLOWS ME TO DO REGIONS LIKE IN C# AND KEEP CODE ORGANIZED. REMOVING IT WOULD BREAK THE ENGINE -->
|
||||
<haxedef name="REGION" />
|
||||
<!-- Comment this out to disable dark mode windows -->
|
||||
<define name="DARK_MODE_WINDOW"/>
|
||||
|
||||
<!--The flixel preloader is not accurate in Chrome. You can use it regularly if you embed the swf into a html file
|
||||
or you can set the actual size of your file manually at "FlxPreloaderBase-onUpdate-bytesTotal"-->
|
||||
<app preloader="flixel.system.FlxPreloader" />
|
||||
<!-- Should it checks for updates? -->
|
||||
<define name="UPDATE_CHECKING" if="desktop" unless="hl || debug"/>
|
||||
|
||||
<!--Minimum without FLX_NO_GAMEPAD: 11.8, without FLX_NO_NATIVE_CURSOR: 11.2-->
|
||||
<set name="SWF_VERSION" value="11.8" />
|
||||
|
||||
<!-- ____________________________ Window Settings ___________________________ -->
|
||||
|
||||
<!--These window settings apply to all targets-->
|
||||
<window width="1280" height="720" background="#000000" hardware="true" vsync="false" />
|
||||
|
||||
<!--HTML5-specific-->
|
||||
<window if="html5" resizable="true" />
|
||||
|
||||
<!--Desktop-specific-->
|
||||
<window if="desktop" orientation="landscape" fullscreen="false" resizable="true" vsync="false"/>
|
||||
<window if="mac" allow-high-dpi="true" />
|
||||
|
||||
<!--Mobile-specific-->
|
||||
<window if="mobile" orientation="landscape" fullscreen="true" width="0" height="0" resizable="false"/>
|
||||
|
||||
<!--Switch-specific-->
|
||||
<window if="switch" orientation="landscape" fullscreen="true" width="0" height="0" resizable="true" />
|
||||
|
||||
<!-- _________________________________ Engine Settings _______________________________ -->
|
||||
|
||||
<!-- Comment this out to disable updates !-->
|
||||
<define name="UPDATE_CHECKING" unless="web || hl || neko || debug"/>
|
||||
|
||||
<!-- Comment this out to disable Discord RPC !-->
|
||||
<section if="cpp">
|
||||
<define name="DISCORD_RPC" if="desktop"/>
|
||||
<!-- Compile With Discord Rich Presence -->
|
||||
<!-- TODO: Update Discord RPC Library & Rewrite DiscordUtil -->
|
||||
<!-- <define name="DISCORD_RPC" if="desktop"/> -->
|
||||
|
||||
<!-- Compile with video support (hxvlc) -->
|
||||
<define name="VIDEO_CUTSCENES" if="desktop || mobile"/>
|
||||
</section>
|
||||
|
||||
<!-- Uncomment this to achive faster compile times, will strip extraneous and commonly unused features. -->
|
||||
<!-- <define name="STRIPPED_COMPILE" unless="debug"/> -->
|
||||
|
||||
<!-- Comment this out to disable modcharting features -->
|
||||
<define name="MODCHARTING_FEATURES" unless="STRIPPED_COMPILE" />
|
||||
<!-- Compile with _theo0p's modcharting tools -->
|
||||
<define name="MODCHARTING_FEATURES" unless="STRIPPED_COMPILE"/>
|
||||
|
||||
<!-- Comment this out to disable GitHub API integration !-->
|
||||
<define name="GITHUB_API" unless="web || hl"/>
|
||||
|
||||
<!-- Comment this out to prevent unused classes from being compiled.
|
||||
Reduces compilation time at the cost of limited HScript flexibility. !-->
|
||||
<define name="COMPILE_ALL_CLASSES" unless="STRIPPED_COMPILE" />
|
||||
<define name="COMPILE_ALL_CLASSES" unless="STRIPPED_COMPILE"/>
|
||||
|
||||
<!-- Comment this out to prevent extending classes in hscript.
|
||||
Reduces compilation time at the cost of limited HScript flexibility. !-->
|
||||
<define name="CUSTOM_CLASSES" unless="STRIPPED_COMPILE" />
|
||||
<define name="CUSTOM_CLASSES" unless="STRIPPED_COMPILE"/>
|
||||
|
||||
<!-- Comment this out to disable abstract support in hscript. !-->
|
||||
<haxedef name="HSCRIPT_ABSTRACT_SUPPORT" unless="STRIPPED_COMPILE" />
|
||||
|
||||
<!-- Comment this out to disable multithreading !-->
|
||||
<haxedef name="ALLOW_MULTITHREADING" unless="web || flash" />
|
||||
<haxedef name="ALLOW_MULTITHREADING" if="cpp"/>
|
||||
|
||||
<!-- Comment this out to disable mods folder !-->
|
||||
<haxedef name="MOD_SUPPORT" unless="web"/>
|
||||
@@ -87,36 +57,60 @@
|
||||
<haxedef name="TRANSLATIONS_SUPPORT"/>
|
||||
|
||||
<!-- Comment this out to disable global scripts !-->
|
||||
<haxedef name="GLOBAL_SCRIPT" />
|
||||
<haxedef name="GLOBAL_SCRIPT"/>
|
||||
|
||||
<!-- Comment this out to disable softcoded states and state scripting !-->
|
||||
<haxedef name="SOFTCODED_STATES" />
|
||||
<haxedef name="SOFTCODED_STATES"/>
|
||||
|
||||
<!-- Comment this out to disable support for custom ndlls. !-->
|
||||
<haxedef name="NDLLS_SUPPORTED" unless="web || iphonesim || hl" />
|
||||
<haxedef name="NDLLS_SUPPORTED" unless="web || iphonesim || hl"/>
|
||||
|
||||
<!-- Comment this out to use the default OpenFL assets system for the assets/ folder. !-->
|
||||
<define name="USE_ADAPTED_ASSETS" unless="web" />
|
||||
<define name="USE_ADAPTED_ASSETS" unless="web"/>
|
||||
|
||||
<!-- Comment this out to disable support for Away3D Flixel Integration (reduces compile times) !-->
|
||||
<define name="THREE_D_SUPPORT" unless="STRIPPED_COMPILE" />
|
||||
<define name="THREE_D_SUPPORT" unless="STRIPPED_COMPILE"/>
|
||||
|
||||
<!-- Comment this out to disable support for Nape integration (flixel.addons.nape) (reduces compile times) !-->
|
||||
<define name="NAPE_ENABLED" unless="STRIPPED_COMPILE" />
|
||||
|
||||
<!-- Comment this out to disable video cutscene support, in case of compilation errors. !-->
|
||||
<section if="cpp">
|
||||
<define name="VIDEO_CUTSCENES" if="desktop || android" />
|
||||
</section>
|
||||
|
||||
<!-- Comment this out to disable commit number on FPS -->
|
||||
<define name="SHOW_BUILD_ON_FPS" />
|
||||
|
||||
<!-- Comment this out to disable dark mode windows -->
|
||||
<define name="DARK_MODE_WINDOW"/>
|
||||
<define name="NAPE_ENABLED" unless="STRIPPED_COMPILE"/>
|
||||
|
||||
<!-- Disable a optimization, to allow reflection to use more functions -->
|
||||
<define name="FLX_NO_GENERIC" />
|
||||
<define name="FLX_NO_GENERIC"/>
|
||||
|
||||
<!-- _________________________ Application Settings _________________________ -->
|
||||
<app
|
||||
title="Friday Night Funkin' - Codename Engine"
|
||||
file="CodenameEngine" packageName="com.yoshman29.codenameengine"
|
||||
package="com.yoshman29.codenameengine"
|
||||
version="1.0.2" company="Yoshman29"/>
|
||||
|
||||
<!--Switch Export with Unique ApplicationID and Icon-->
|
||||
<set name="APP_ID" value="0x0100f6c013bbc000"/>
|
||||
|
||||
<!--<set name="SWF_VERSION" value="11.8"/>-->
|
||||
|
||||
<!--Place custom nodes like icons here (higher priority to override the HaxeFlixel icon)-->
|
||||
<icon path="art/icon16.png" size='16'/>
|
||||
<icon path="art/icon32.png" size='24'/>
|
||||
<icon path="art/icon32.png" size='32'/>
|
||||
<icon path="art/icon64.png" size='40'/>
|
||||
<icon path="art/icon64.png" size='48'/>
|
||||
<icon path="art/icon64.png" size='64'/>
|
||||
<icon path="art/iconOG.png" size='96'/>
|
||||
<icon path="art/iconOG.png" size='128'/>
|
||||
<icon path="art/iconOG.png" size='196'/>
|
||||
<icon path="art/iconOG.png" size='256'/>
|
||||
<icon path="art/iconOG.png" size='512'/>
|
||||
<icon path="art/iconOG.png" size='768'/>
|
||||
<icon path="art/iconOG.png" size='1024'/>
|
||||
|
||||
<!-- ____________________________ Window Settings ___________________________ -->
|
||||
|
||||
<window hardware="true" colorDepth="32" background="" fps="0" orientation="landscape"/>
|
||||
<window unless="desktop" width="0" height="0" vsync="true" fullscreen="true"/>
|
||||
<window if="desktop" type="opengl" width="1280" height="720" fullscreen="false" resizable="true" vsync="false" allowHighDPI="true"/>
|
||||
<window if="mobile" resizable="false"/>
|
||||
<window if="html5 || switch" resizable="true"/>
|
||||
|
||||
<!-- _______________________________ Libraries ______________________________ -->
|
||||
|
||||
@@ -124,32 +118,46 @@
|
||||
<define name="disable-version-check"/>
|
||||
<haxedef name="disable-version-check"/>
|
||||
|
||||
<haxedef name="no-deprecation-warnings"/>
|
||||
<haxedef name="DISCORD_DISABLE_IO_THREAD" if="DISCORD_RPC"/>
|
||||
<haxelib name="flixel"/>
|
||||
|
||||
<haxelib name="flixel" />
|
||||
<haxelib name="flixel-addons" />
|
||||
<haxelib name="flixel-addons"/>
|
||||
<haxelib name="flixel-animate"/>
|
||||
|
||||
<haxelib name="hxvlc" if="VIDEO_CUTSCENES" />
|
||||
<haxelib name="away3d" if="THREE_D_SUPPORT" />
|
||||
<haxelib name="format" />
|
||||
<haxelib name="flixel-animate" />
|
||||
<haxelib name="hscript-improved"/>
|
||||
|
||||
<haxelib name="format" unless="STRIPPED_COMPILE"/>
|
||||
<haxelib name="markdown" unless="STRIPPED_COMPILE"/>
|
||||
|
||||
<haxelib name="hxvlc" if="VIDEO_CUTSCENES"/>
|
||||
<haxelib name="hxdiscord_rpc" if="DISCORD_RPC"/>
|
||||
<haxelib name="away3d" if="THREE_D_SUPPORT"/>
|
||||
<haxelib name="nape-haxe4" if="NAPE_ENABLED"/>
|
||||
|
||||
<haxelib name="hscript-improved" />
|
||||
<haxelib name="hxdiscord_rpc" if="DISCORD_RPC"/>
|
||||
<haxelib name="markdown" />
|
||||
<haxelib name="hxcpp-debug-server" if="TEST_BUILD debug" />
|
||||
<haxelib name="hxcpp-debug-server" if="debug"/>
|
||||
|
||||
<!-- _____________________________ Path Settings ____________________________ -->
|
||||
<!-- ____________________________ Source Settings ___________________________ -->
|
||||
|
||||
<set name="BUILD_DIR" value="export/debug" if="debug" />
|
||||
<set name="BUILD_DIR" value="export/release" unless="debug" />
|
||||
<set name="BUILD_DIR" value="export/debug-32bit" if="debug 32bit" />
|
||||
<set name="BUILD_DIR" value="export/release-32bit" if="32bit" unless="debug" />
|
||||
<set name="BUILD_DIR" value="latest/" if="beta" />
|
||||
<!-- DO NO REMOVE THIS -->
|
||||
<haxedef name="REGION"/>
|
||||
|
||||
<source name="source" />
|
||||
<set name="BUILD_DIR" value="export/debug" if="debug"/>
|
||||
<set name="BUILD_DIR" value="export/release" unless="debug"/>
|
||||
<set name="BUILD_DIR" value="export/debug-32bit" if="debug 32bit"/>
|
||||
<set name="BUILD_DIR" value="export/release-32bit" if="32bit" unless="debug"/>
|
||||
<set name="BUILD_DIR" value="latest/" if="beta"/>
|
||||
|
||||
<app main="funkin.backend.system.Main" preloader="flixel.system.FlxPreloader"/>
|
||||
<source name="source"/>
|
||||
|
||||
<section if="MODCHARTING_FEATURES">
|
||||
<haxedef name="FM_ENGINE" value="CODENAME"/>
|
||||
<haxedef name="FM_ENGINE_VERSION" value="1.0"/>
|
||||
|
||||
<haxelib name="funkin-modchart" />
|
||||
<haxeflag name="--macro" value="modchart.backend.macros.Macro.includeFiles()"/>
|
||||
</section>
|
||||
|
||||
<!-- ____________________________ Assets Settings ___________________________ -->
|
||||
|
||||
<define name="PRELOAD_ALL" unless="web" />
|
||||
<define name="NO_PRELOAD_ALL" unless="PRELOAD_ALL"/>
|
||||
@@ -165,81 +173,69 @@
|
||||
<assets path='mods' rename='mods' embed='false' />
|
||||
</section>
|
||||
|
||||
<assets path='art/readme.txt' rename='do NOT readme.txt' />
|
||||
|
||||
<!-- OpenAL config -->
|
||||
<section if="desktop">
|
||||
<assets path="building/alsoft.txt" rename="plugins/alsoft.ini" type="text" if="windows"/>
|
||||
<assets path="building/alsoft.txt" rename="plugins/alsoft.conf" type="text" unless="windows"/>
|
||||
</section>
|
||||
<!-- <assets path='art/readme.txt' rename='do NOT readme.txt' /> -->
|
||||
|
||||
<!-- ______________________________ Haxedefines _____________________________ -->
|
||||
|
||||
<haxeflag name="--macro" value="funkin.backend.system.macros.BuildInfo.printBuildInfo()" />
|
||||
<haxeflag name="--macro" value="funkin.backend.system.macros.Macros.initMacros()"/>
|
||||
|
||||
<haxedef name="FLX_NO_FOCUS_LOST_SCREEN" />
|
||||
<haxedef name="VIDEO_CUTSCENES" if="VIDEO_CUTSCENES"/>
|
||||
<haxedef name="UPDATE_CHECKING" if="UPDATE_CHECKING"/>
|
||||
<haxedef name="DISCORD_RPC" if="DISCORD_RPC"/>
|
||||
<haxedef name="SHOW_BUILD_ON_FPS" if="SHOW_BUILD_ON_FPS"/>
|
||||
<haxedef name="SOFTCODED_CLASSES" if="SOFTCODED_CLASSES"/>
|
||||
<haxedef name="USE_ADAPTED_ASSETS" if="USE_ADAPTED_ASSETS"/>
|
||||
|
||||
<!--Disable the Flixel core debugger. Automatically gets set whenever you compile in release mode!-->
|
||||
<haxedef name="FLX_NO_DEBUG" unless="debug" />
|
||||
<!-- Backward Compatibilities -->
|
||||
<haxedef name="FLX_HEALTH" />
|
||||
|
||||
<haxedef name="analyzer-optimize" unless="debug" />
|
||||
<haxedef name="FLX_NO_FOCUS_LOST_SCREEN"/>
|
||||
<haxedef name="FLX_NO_DEBUG" unless="debug"/>
|
||||
|
||||
<haxedef name="VIDEO_CUTSCENES" if="VIDEO_CUTSCENES" />
|
||||
<haxedef name="UPDATE_CHECKING" if="UPDATE_CHECKING" />
|
||||
<haxedef name="DISCORD_RPC" if="DISCORD_RPC" />
|
||||
<haxedef name="SHOW_BUILD_ON_FPS" if="SHOW_BUILD_ON_FPS" />
|
||||
<haxedef name="SOFTCODED_CLASSES" if="SOFTCODED_CLASSES" />
|
||||
<haxedef name="USE_ADAPTED_ASSETS" if="USE_ADAPTED_ASSETS" />
|
||||
<haxedef name="openfl_dpi_aware" if="openfl_dpi_aware" />
|
||||
<haxedef name="DISCORD_DISABLE_IO_THREAD" if="DISCORD_RPC"/>
|
||||
|
||||
<!-- Disable it for more concise compilation errors. -->
|
||||
<haxedef name="message.reporting" value="pretty" />
|
||||
|
||||
<!-- _________________________________ Custom _______________________________ -->
|
||||
|
||||
<!--Place custom nodes like icons here (higher priority to override the HaxeFlixel icon)-->
|
||||
<icon path="art/icon16.png" size='16'/>
|
||||
<icon path="art/icon32.png" size='24'/>
|
||||
<icon path="art/icon32.png" size='32'/>
|
||||
<icon path="art/icon64.png" size='40'/>
|
||||
<icon path="art/icon64.png" size='48'/>
|
||||
<icon path="art/icon64.png" size='64'/>
|
||||
<icon path="art/iconOG.png" size='96' />
|
||||
<icon path="art/iconOG.png" size='128' />
|
||||
<icon path="art/iconOG.png" size='196' />
|
||||
<icon path="art/iconOG.png" size='256' />
|
||||
<icon path="art/iconOG.png" size='512' />
|
||||
<icon path="art/iconOG.png" size='768' />
|
||||
<icon path="art/iconOG.png" size='1024' />
|
||||
|
||||
<!-- _________________________________ Additional stuff _______________________________ -->
|
||||
|
||||
<section if="MODCHARTING_FEATURES">
|
||||
<haxedef name="FM_ENGINE" value="CODENAME"/>
|
||||
<haxedef name="FM_ENGINE_VERSION" value="1.0"/>
|
||||
|
||||
<haxelib name="funkin-modchart" />
|
||||
<haxeflag name="--macro" value="modchart.backend.macros.Macro.includeFiles()"/>
|
||||
</section>
|
||||
|
||||
<section if="COMPILE_ALL_CLASSES">
|
||||
<haxeflag name="-dce" value="no" />
|
||||
<haxeflag name="--macro" value="funkin.backend.system.macros.Macros.addAdditionalClasses()" />
|
||||
</section>
|
||||
|
||||
<haxeflag name="--macro" value="funkin.backend.system.macros.Macros.initMacros()" />
|
||||
<!-- <haxedef name="no-deprecation-warnings"/> -->
|
||||
|
||||
<!-- Fix compiling issues -->
|
||||
<setenv name="HAXEPATH" value="./"/>
|
||||
|
||||
<haxedef name="HXCPP_M32" if="32bit" />
|
||||
<haxedef name="HXCPP_DEBUG_LINK" if="debug" />
|
||||
<haxedef name="HXCPP_CHECK_POINTER" />
|
||||
<haxedef name="HXCPP_STACK_LINE" />
|
||||
<haxedef name="HXCPP_CATCH_SEGV" />
|
||||
<!-- Haxe 4.3.0+: Enable pretty syntax errors and stuff. -->
|
||||
<haxedef name="message.reporting" value="pretty"/>
|
||||
|
||||
<haxedef name="hscriptPos" /> <!-- for logging -->
|
||||
<!-- for logging -->
|
||||
<haxedef name="hscriptPos"/>
|
||||
|
||||
<section if="COMPILE_ALL_CLASSES">
|
||||
<haxeflag name="-dce" value="no"/>
|
||||
<haxeflag name="--macro" value="funkin.backend.system.macros.Macros.addAdditionalClasses()"/>
|
||||
</section>
|
||||
|
||||
<haxeflag name="--macro" value="funkin.backend.system.macros.BuildInfo.printBuildInfo()"/>
|
||||
|
||||
<!-- EXPERMENTAL FEATURES -->
|
||||
<haxedef name="EXPERIMENTAL_FLXGRAPHIC_DESTROY_FIX" />
|
||||
<haxedef name="openfl_dpi_aware"/>
|
||||
<haxedef name="openfl_pool_events"/>
|
||||
<haxedef name="openfl-enable-handle-error"/>
|
||||
<haxedef name="openfl_no_custom_render_event"/>
|
||||
|
||||
<haxedef name="HXCPP_M32" if="32bit"/>
|
||||
<haxedef name="HXCPP_DEBUG_LINK" if="debug"/>
|
||||
<!--C++20 seems doesn't seem to do anything but causes compile warnings-->
|
||||
<!--<haxedef name="HXCPP_CPP20"/>-->
|
||||
<!--<haxedef name="HXCPP_CLANG"/>-->
|
||||
<!--<haxedef name="HXCPP_MSVC"/>-->
|
||||
<!-- This seems to causes more harm, but to be fair, its mostly because of text rendering -->
|
||||
<!-- This is really important to fix honestly, try to get cairo text rendering not memory leak in the future -->
|
||||
<!--<haxedef name="HXCPP_GC_BIG_BLOCKS"/>-->
|
||||
<!--<haxedef name="HXCPP_GC_GENERATIONAL"/>-->
|
||||
<!--<haxedef name="HXCPP_GC_DYNAMIC_SIZE"/>-->
|
||||
<haxedef name="HXCPP_CHECK_POINTER"/>
|
||||
<haxedef name="HXCPP_STACK_LINE"/>
|
||||
<haxedef name="HXCPP_STACK_TRACE"/>
|
||||
<!--<haxedef name="HXCPP_CATCH_SEGV"/>-->
|
||||
<haxedef name="HXCPP_FAST_LINK" unless="debug"/>
|
||||
<!--<haxedef name="HXCPP_ALIGN_ALLOC"/>--> <!--hxcpp.h(71): warning C4005: 'HXCPP_ALIGN_ALLOC': macro redefinition-->
|
||||
|
||||
<haxedef name="analyzer-optimize" unless="debug"/>
|
||||
</project>
|
||||
|
||||
@@ -1,960 +0,0 @@
|
||||
package flixel.sound;
|
||||
|
||||
import lime.media.AudioBuffer;
|
||||
import lime.media.AudioSource;
|
||||
import lime.media.AudioManager;
|
||||
|
||||
import openfl.Assets;
|
||||
import openfl.events.IEventDispatcher;
|
||||
import openfl.events.Event;
|
||||
import openfl.media.Sound;
|
||||
import openfl.media.SoundChannel;
|
||||
import openfl.media.SoundTransform;
|
||||
import openfl.media.SoundMixer;
|
||||
import openfl.net.URLRequest;
|
||||
import openfl.utils.AssetType;
|
||||
#if flash11
|
||||
import openfl.utils.ByteArray;
|
||||
#end
|
||||
|
||||
import flixel.math.FlxMath;
|
||||
import flixel.math.FlxPoint;
|
||||
import flixel.system.FlxAssets.FlxSoundAsset;
|
||||
import flixel.tweens.FlxTween;
|
||||
import flixel.util.FlxDestroyUtil;
|
||||
import flixel.util.FlxSignal;
|
||||
import flixel.util.FlxStringUtil;
|
||||
import flixel.FlxBasic;
|
||||
import flixel.FlxObject;
|
||||
import flixel.FlxG;
|
||||
|
||||
@:access(flixel.FlxGame)
|
||||
class FlxSound extends FlxBasic {
|
||||
/**
|
||||
* The x position of this sound in world coordinates.
|
||||
* Only really matters if you are doing proximity/panning stuff.
|
||||
*/
|
||||
public var x:Float;
|
||||
|
||||
/**
|
||||
* The y position of this sound in world coordinates.
|
||||
* Only really matters if you are doing proximity/panning stuff.
|
||||
*/
|
||||
public var y:Float;
|
||||
|
||||
/**
|
||||
* Whether or not this sound should be automatically destroyed when you switch states.
|
||||
*/
|
||||
public var persist:Bool;
|
||||
|
||||
/**
|
||||
* Whether or not the sound is currently playing.
|
||||
*/
|
||||
public var playing(get, never):Bool;
|
||||
|
||||
/**
|
||||
* Set volume to a value between 0 and 1* to change how this sound is.
|
||||
*/
|
||||
public var volume(get, set):Float;
|
||||
|
||||
/**
|
||||
* Whether to make this sound muted or not.
|
||||
*/
|
||||
public var muted(default, set):Bool;
|
||||
|
||||
#if FLX_PITCH
|
||||
/**
|
||||
* Set pitch, which also alters the playback speed. Default is 1.
|
||||
* @since 5.0.0
|
||||
*/
|
||||
public var pitch(get, set):Float;
|
||||
|
||||
/**
|
||||
* Alters the pitch of the sound depends on the current FlxG.timeScale. Default is true.
|
||||
* @since raltyMod
|
||||
*/
|
||||
public var timeScaleBased:Bool;
|
||||
#end
|
||||
|
||||
/**
|
||||
* Pan amount. -1 = full left, 1 = full right. Proximity based panning overrides this.
|
||||
*/
|
||||
public var pan(get, set):Float;
|
||||
|
||||
/**
|
||||
* The position in runtime of the music playback in milliseconds.
|
||||
* If set while paused, changes only come into effect after a `resume()` call.
|
||||
*/
|
||||
public var time(get, set):Float;
|
||||
|
||||
/**
|
||||
* The offset for this FlxSound.
|
||||
* Useful for just generally offsetting this sound without affecting time.
|
||||
* @since raltyMod
|
||||
*/
|
||||
public var offset(get, set):Float;
|
||||
|
||||
/**
|
||||
* The length of the sound in milliseconds.
|
||||
* @since 4.2.0
|
||||
*/
|
||||
public var length(get, never):Float;
|
||||
|
||||
/**
|
||||
* The latency of the sound in milliseconds.
|
||||
* @since raltyMod
|
||||
*/
|
||||
//public var latency(get, never):Float;
|
||||
|
||||
/**
|
||||
* Whether or not this sound should loop.
|
||||
*/
|
||||
public var looped(default, set):Bool;
|
||||
|
||||
/**
|
||||
* In case of looping, the point (in milliseconds) from where to restart the sound when it loops back
|
||||
* @since 4.1.0
|
||||
*/
|
||||
public var loopTime(default, set):Float;
|
||||
|
||||
/**
|
||||
* At which point to stop playing the sound, in milliseconds.
|
||||
* If not set / `null`, the sound completes normally.
|
||||
* @since 4.2.0
|
||||
*/
|
||||
public var endTime(default, set):Null<Float>;
|
||||
|
||||
/**
|
||||
* The sound's "target" (for proximity and panning).
|
||||
* @since raltyMod
|
||||
*/
|
||||
public var target:Null<FlxObject>;
|
||||
|
||||
/**
|
||||
* The maximum effective radius of this sound (for proximity and panning).
|
||||
* @since raltyMod
|
||||
*/
|
||||
public var radius:Float;
|
||||
|
||||
/**
|
||||
* Whether the proximity alters the pan or not.
|
||||
* @since raltyMod
|
||||
*/
|
||||
public var proximityPan:Bool;
|
||||
|
||||
/**
|
||||
* Controls how much this object is affected by camera scrolling. `0` = no movement (e.g. a static sound),
|
||||
* This is only useful if used with proximity (Initialized once proximity is used).
|
||||
* @since raltyMod
|
||||
*/
|
||||
public var scrollFactor(default, null):FlxPoint;
|
||||
|
||||
/**
|
||||
* Stores for how much channels are in the loaded sound.
|
||||
* @since raltyMod
|
||||
*/
|
||||
public var channels(get, never):Int;
|
||||
|
||||
/**
|
||||
* Whether or not this sound is stereo instead of mono.
|
||||
* @since raltyMod
|
||||
*/
|
||||
public var stereo(get, never):Bool;
|
||||
|
||||
/**
|
||||
* Whether or not this sound is loaded yet.
|
||||
* @since raltyMod
|
||||
*/
|
||||
public var loaded(get, never):Bool;
|
||||
|
||||
/**
|
||||
* Whether or not this sound is streamed, only vorbis though.
|
||||
* @since raltyMod
|
||||
*/
|
||||
public var streamed(get, never):Bool;
|
||||
|
||||
/**
|
||||
* Stores the average wave amplitude of both stereo channels.
|
||||
* @since raltyMod
|
||||
*/
|
||||
public var amplitude(get, never):Float;
|
||||
|
||||
/**
|
||||
* Just the amplitude of the left stereo channel
|
||||
* @since raltyMod
|
||||
*/
|
||||
public var amplitudeLeft(get, never):Float;
|
||||
|
||||
/**
|
||||
* Just the amplitude of the right stereo channel
|
||||
* @since raltyMod
|
||||
*/
|
||||
public var amplitudeRight(get, never):Float;
|
||||
|
||||
/**
|
||||
* The ID3 song name. Defaults to null. Currently only works for MP3 streamed sounds.
|
||||
*/
|
||||
public var name(default, null):String;
|
||||
|
||||
/**
|
||||
* The ID3 artist name. Defaults to null. Currently only works for MP3 streamed sounds.
|
||||
*/
|
||||
public var artist(default, null):String;
|
||||
|
||||
/**
|
||||
* Whether to call `destroy()` when the sound has finished playing.
|
||||
*/
|
||||
public var autoDestroy:Bool;
|
||||
|
||||
/**
|
||||
* Signal that is dispatched on sound complete.
|
||||
* Seperates the looping from onComplete.
|
||||
*/
|
||||
public final onFinish:FlxSignal;
|
||||
|
||||
/**
|
||||
* Tracker for sound complete callback. If assigned, will be called
|
||||
* each time when sound reaches its end.
|
||||
*/
|
||||
//@:deprecated("`FlxSound.onComplete` is deprecated! Use `FlxSound.onFinish` instead.")
|
||||
public var onComplete:Void->Void;
|
||||
|
||||
/**
|
||||
* The sound group this sound belongs to
|
||||
*/
|
||||
public var group(default, set):FlxSoundGroup;
|
||||
|
||||
/**
|
||||
* Stores the sound lime AudioBuffer if exists.
|
||||
* @since raltyMod
|
||||
*/
|
||||
public var buffer(get, never):AudioBuffer;
|
||||
|
||||
/**
|
||||
* The tween used to fade this sound's volume in and out (set via `fadeIn()` and `fadeOut()`)
|
||||
* @since 4.1.0
|
||||
*/
|
||||
public var fadeTween:FlxTween;
|
||||
|
||||
@:allow(flixel.system.frontEnds.SoundFrontEnd.load) var _sound:Sound;
|
||||
var _transform:SoundTransform;
|
||||
var _channel:SoundChannel;
|
||||
var _source:AudioSource;
|
||||
var _paused:Bool;
|
||||
var _volume:Float;
|
||||
var _volumeAdjust:Float;
|
||||
var _pan:Float;
|
||||
var _panAdjust:Float;
|
||||
var _time:Float;
|
||||
var _offset:Float;
|
||||
var _timeInterpolation:Float;
|
||||
var _lastTime:Null<Float>; // FlxG.game.getTicks(), in MS
|
||||
var _length:Float;
|
||||
#if FLX_PITCH
|
||||
var _pitch:Float;
|
||||
var _timeScaleAdjust:Float;
|
||||
var _realPitch:Float;
|
||||
#end
|
||||
var _amplitudeLeft:Float;
|
||||
var _amplitudeRight:Float;
|
||||
var _amplitudeUpdate:Bool;
|
||||
var _alreadyPaused:Null<Bool>;
|
||||
|
||||
public function new() {
|
||||
super();
|
||||
onFinish = new FlxSignal();
|
||||
revive();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets this FlxSound properties.
|
||||
*
|
||||
* @param clean Whether if this FlxSound also needs to be cleaned up too.
|
||||
*/
|
||||
public function reset():Void {
|
||||
if (_source != null) stop();
|
||||
onFinish.removeAll();
|
||||
|
||||
x = y = 0;
|
||||
@:bypassAccessor muted = false;
|
||||
@:bypassAccessor looped = false;
|
||||
@:bypassAccessor loopTime = 0;
|
||||
@:bypassAccessor endTime = null;
|
||||
autoDestroy = false;
|
||||
visible = false;
|
||||
target = null;
|
||||
radius = 0;
|
||||
proximityPan = true;
|
||||
onComplete = null;
|
||||
timeScaleBased = true;
|
||||
|
||||
_alreadyPaused = null;
|
||||
_cameras = null;
|
||||
_lastTime = null;
|
||||
_paused = false;
|
||||
_offset = _length = _time = 0;
|
||||
_volume = _volumeAdjust = 1;
|
||||
_amplitudeLeft = _amplitudeRight = 0;
|
||||
_amplitudeUpdate = true;
|
||||
#if FLX_PITCH _pitch = _realPitch = _timeScaleAdjust = 1; #end
|
||||
|
||||
if (_transform == null) _transform = new SoundTransform();
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal cleanup function for cleaning up this FlxSound.
|
||||
* @since raltyMod
|
||||
*/
|
||||
function cleanup(destroySound:Bool, resetPosition:Bool = true) @:privateAccess {
|
||||
active = false;
|
||||
_lastTime = null;
|
||||
|
||||
if (destroySound) {
|
||||
scrollFactor = FlxDestroyUtil.put(scrollFactor);
|
||||
|
||||
if (group != null) group.remove(this);
|
||||
|
||||
if (_channel != null) {
|
||||
_channel.stop();
|
||||
_channel = null;
|
||||
}
|
||||
if (_source != null) {
|
||||
_source.onComplete.remove(stopped);
|
||||
_source.onLoop.remove(source_looped);
|
||||
_source.dispose();
|
||||
}
|
||||
_source = null;
|
||||
_sound = null;
|
||||
|
||||
if (autoDestroy) persist = false;
|
||||
reset();
|
||||
}
|
||||
else if (_channel != null && _channel.__isValid) {
|
||||
if (resetPosition) {
|
||||
_source.stop();
|
||||
|
||||
_time = 0;
|
||||
_paused = false;
|
||||
}
|
||||
else if (!_paused) {
|
||||
get_time();
|
||||
_source.pause();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles fade out, fade in, panning, proximity, and amplitude operations each frame.
|
||||
*/
|
||||
override function update(elapsed:Float):Void {
|
||||
if (!playing) return;
|
||||
|
||||
var timeScaleTarget = timeScaleBased ? FlxG.timeScale : 1.0;
|
||||
if (_timeScaleAdjust != timeScaleTarget) {
|
||||
_timeScaleAdjust = timeScaleTarget;
|
||||
pitch = _pitch;
|
||||
if (_channel == null) return;
|
||||
}
|
||||
|
||||
_amplitudeUpdate = true;
|
||||
|
||||
// Distance-based volume control
|
||||
if (target != null) {
|
||||
var targetPosition = target.getPosition(), position = getPosition();
|
||||
var camera = camera;
|
||||
if (camera != null) {
|
||||
targetPosition.subtract(camera.scroll.x * target.scrollFactor.x, camera.scroll.y * target.scrollFactor.y);
|
||||
if (scrollFactor != null) position.subtract(camera.scroll.x * scrollFactor.x, camera.scroll.y * scrollFactor.y);
|
||||
else position.subtract(camera.scroll.x, camera.scroll.y);
|
||||
}
|
||||
|
||||
var radialMultiplier = targetPosition.distanceTo(position) / radius;
|
||||
|
||||
// Make it so it affects the 3d position of the source and not just the panning?
|
||||
_volumeAdjust = 1 - FlxMath.bound(radialMultiplier, 0, 1);
|
||||
if (proximityPan) _panAdjust = (position.x - targetPosition.x) / radius;
|
||||
|
||||
targetPosition.put();
|
||||
position.put();
|
||||
}
|
||||
else
|
||||
_volumeAdjust = 1.0;
|
||||
|
||||
updateTransform();
|
||||
}
|
||||
|
||||
override function revive() {
|
||||
reset();
|
||||
super.revive();
|
||||
}
|
||||
|
||||
override function kill() {
|
||||
super.kill();
|
||||
reset();
|
||||
}
|
||||
|
||||
override function destroy() {
|
||||
super.destroy();
|
||||
cleanup(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* One of the main setup functions for sounds, this function loads a sound from an embedded MP3.
|
||||
*
|
||||
* @param embeddedSound An embedded Class object representing an MP3 file.
|
||||
* @param looped Whether or not this sound should loop endlessly.
|
||||
* @param autoDestroy Whether or not this FlxSound instance should be destroyed when the sound finishes playing.
|
||||
* Default value is false, but `FlxG.sound.play()` and `FlxG.sound.stream()` will set it to true by default.
|
||||
* @param onComplete Called when the sound finished playing
|
||||
* @return This FlxSound instance (nice for chaining stuff together, if you're into that).
|
||||
*/
|
||||
public function loadEmbedded(embeddedSound:FlxSoundAsset, looped = false, autoDestroy = false, ?onComplete:Void->Void):FlxSound {
|
||||
if (!exists || embeddedSound == null) return this;
|
||||
cleanup(true);
|
||||
|
||||
if ((embeddedSound is Sound)) _sound = embeddedSound;
|
||||
else if ((embeddedSound is Class)) _sound = Type.createInstance(embeddedSound, []);
|
||||
else if ((embeddedSound is String)) {
|
||||
if (Assets.exists(embeddedSound, AssetType.MUSIC) && this == FlxG.sound.music)
|
||||
_sound = Assets.getMusic(embeddedSound);
|
||||
else if (Assets.exists(embeddedSound, AssetType.SOUND))
|
||||
_sound = Assets.getSound(embeddedSound);
|
||||
else
|
||||
FlxG.log.error('Could not find a Sound asset with an ID of \'$embeddedSound\'.');
|
||||
}
|
||||
|
||||
return init(looped, autoDestroy, onComplete);
|
||||
}
|
||||
|
||||
/**
|
||||
* One of the main setup functions for sounds, this function loads a sound from a URL.
|
||||
*
|
||||
* @param soundURL A string representing the URL of the MP3 file you want to play.
|
||||
* @param looped Whether or not this sound should loop endlessly.
|
||||
* @param autoDestroy Whether or not this FlxSound instance should be destroyed when the sound finishes playing.
|
||||
* Default value is false, but `FlxG.sound.play()` and `FlxG.sound.stream()` will set it to true by default.
|
||||
* @param onComplete Called when the sound finished playing
|
||||
* @param onLoad Called when the sound finished loading.
|
||||
* @return This FlxSound instance (nice for chaining stuff together, if you're into that).
|
||||
*/
|
||||
public function loadStream(soundURL:String, looped = false, autoDestroy = false, ?onComplete:Void->Void, ?onLoad:Void->Void):FlxSound {
|
||||
if (!exists) return this;
|
||||
cleanup(true);
|
||||
|
||||
_sound = new Sound();
|
||||
_sound.addEventListener(Event.ID3, gotID3);
|
||||
var loadCallback:Event->Void = null;
|
||||
loadCallback = function(e:Event)
|
||||
{
|
||||
(e.target : IEventDispatcher).removeEventListener(e.type, loadCallback);
|
||||
// Check if the sound was destroyed before calling. Weak ref doesn't guarantee GC.
|
||||
if (_sound == e.target)
|
||||
{
|
||||
_length = _sound.length;
|
||||
if (onLoad != null)
|
||||
onLoad();
|
||||
}
|
||||
}
|
||||
// Use a weak reference so this can be garbage collected if destroyed before loading.
|
||||
_sound.addEventListener(Event.COMPLETE, loadCallback, false, 0, true);
|
||||
_sound.load(new URLRequest(soundURL));
|
||||
|
||||
return init(looped, autoDestroy, onComplete);
|
||||
}
|
||||
|
||||
#if flash11
|
||||
/**
|
||||
* One of the main setup functions for sounds, this function loads a sound from a ByteArray.
|
||||
*
|
||||
* @param bytes A ByteArray object.
|
||||
* @param looped Whether or not this sound should loop endlessly.
|
||||
* @param autoDestroy Whether or not this FlxSound instance should be destroyed when the sound finishes playing.
|
||||
* Default value is false, but `FlxG.sound.play()` and `FlxG.sound.stream()` will set it to true by default.
|
||||
* @return This FlxSound instance (nice for chaining stuff together, if you're into that).
|
||||
*/
|
||||
public function loadByteArray(bytes:ByteArray, looped = false, autoDestroy = false, ?onComplete:Void->Void):FlxSound {
|
||||
if (!exists) return this;
|
||||
cleanup(true);
|
||||
|
||||
_sound = new Sound();
|
||||
_sound.addEventListener(Event.ID3, gotID3);
|
||||
_sound.loadCompressedDataFromByteArray(bytes, bytes.length);
|
||||
|
||||
return init(looped, autoDestroy, onComplete);
|
||||
}
|
||||
#end
|
||||
|
||||
function init(?looped:Null<Bool>, ?autoDestroy:Null<Bool>, ?onComplete:Null<Void->Void>):FlxSound {
|
||||
if (looped != null) this.looped = looped;
|
||||
if (autoDestroy != null) this.autoDestroy = autoDestroy;
|
||||
if (onComplete != null) this.onComplete = onComplete;
|
||||
|
||||
if (_sound != null) makeChannel();
|
||||
else _length = 0;
|
||||
|
||||
endTime = null;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call inbetween init function if a sound asset does exists.
|
||||
* @since raltyMod
|
||||
*/
|
||||
function makeChannel() @:privateAccess {
|
||||
if (_sound.__buffer == null) return;
|
||||
if (_source != null) _source.dispose();
|
||||
|
||||
if (_channel == null) (_source = (_channel = new SoundChannel(null)).__source = new AudioSource(_sound.__buffer)).gain = 0;
|
||||
else {
|
||||
(_source = new AudioSource(_sound.__buffer)).gain = 0;
|
||||
_channel.__dispose();
|
||||
_channel.__source = _source;
|
||||
SoundMixer.__registerSoundChannel(_channel);
|
||||
}
|
||||
_length = _source.length;
|
||||
|
||||
_source.onComplete.add(stopped);
|
||||
_source.onLoop.add(source_looped);
|
||||
_channel.__soundTransform = _transform;
|
||||
_channel.__isValid = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call after adjusting the volume to update the sound channel's settings.
|
||||
*/
|
||||
@:allow(flixel.sound.FlxSoundGroup)
|
||||
function updateTransform() {
|
||||
_transform.volume = calcTransformVolume();
|
||||
_transform.pan = _pan + _panAdjust;
|
||||
if (_channel != null) _channel.soundTransform = _transform;
|
||||
}
|
||||
|
||||
public function calcTransformVolume():Float {
|
||||
if (muted) return 0.0;
|
||||
|
||||
#if FLX_SOUND_SYSTEM
|
||||
if (FlxG.sound.muted) return 0.0;
|
||||
|
||||
// TODO: when flixel-cne is updated, enable this
|
||||
//return FlxG.sound.applySoundCurve(FlxG.sound.volume * volume);
|
||||
return FlxG.sound.volume * getActualVolume();
|
||||
#else
|
||||
return getActualVolume();
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this function if you want this sound's volume to change
|
||||
* based on distance from a particular FlxObject.
|
||||
*
|
||||
* @param X The X position of the sound.
|
||||
* @param Y The Y position of the sound.
|
||||
* @param TargetObject The object you want to track.
|
||||
* @param Radius The maximum distance this sound can travel.
|
||||
* @param Pan Whether panning should be used in addition to the volume changes.
|
||||
* @return This FlxSound instance (nice for chaining stuff together, if you're into that).
|
||||
*/
|
||||
public function proximity(x = 0.0, y = 0.0, ?targetObject:FlxObject, ?radius:Float, pan = true, ?scrollFactor:FlxPoint):FlxSound {
|
||||
setPosition(x, y);
|
||||
if (targetObject != null) this.target = targetObject;
|
||||
if (radius != null) this.radius = radius;
|
||||
proximityPan = pan;
|
||||
|
||||
if (this.scrollFactor == null) this.scrollFactor = FlxPoint.get(1, 1);
|
||||
if (scrollFactor != null) this.scrollFactor.copyFrom(scrollFactor);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to set the coordinates of this object.
|
||||
* Sound positioning is used in conjunction with proximity/panning.
|
||||
*
|
||||
* @param x The new x position
|
||||
* @param y The new y position
|
||||
*/
|
||||
public function setPosition(x = 0.0, y = 0.0):Void {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the world position of this object.
|
||||
*
|
||||
* @param result Optional arg for the returning point.
|
||||
* @return The world position of this object.
|
||||
* @since raltyMod
|
||||
*/
|
||||
public function getPosition(?result:FlxPoint):FlxPoint {
|
||||
if (result == null)
|
||||
result = FlxPoint.get();
|
||||
|
||||
return result.set(x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this function to play the sound - also works on paused sounds.
|
||||
*
|
||||
* @param forceRestart Whether to start the sound over or not.
|
||||
* Default value is false, meaning if the sound is already playing or was
|
||||
* paused when you call play(), it will continue playing from its current
|
||||
* position, NOT start again from the beginning.
|
||||
* @param startTime At which point to start playing the sound, in milliseconds.
|
||||
* @param endTime At which point to stop playing the sound, in milliseconds.
|
||||
* If not set / `null`, the sound completes normally.
|
||||
*/
|
||||
public function play(forceRestart = false, startTime = 0.0, ?endTime:Null<Float>):FlxSound {
|
||||
if (!exists) return this;
|
||||
|
||||
if (forceRestart) cleanup(false, true);
|
||||
else if (playing) return this;
|
||||
|
||||
if (endTime != null) this.endTime = endTime;
|
||||
if (_paused) resume();
|
||||
else startSound(startTime);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unpause a sound. Only works on sounds that have been paused.
|
||||
* @return This FlxSound instance (nice for chaining stuff together, if you're into that).
|
||||
*/
|
||||
public function resume():FlxSound {
|
||||
if (_paused) startSound(_time);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this function to pause this sound.
|
||||
* @return This FlxSound instance (nice for chaining stuff together, if you're into that).
|
||||
*/
|
||||
public function pause():FlxSound {
|
||||
if (!playing) return this;
|
||||
|
||||
cleanup(false, false);
|
||||
_paused = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this function to stop this sound.
|
||||
* @return This FlxSound instance (nice for chaining stuff together, if you're into that).
|
||||
*/
|
||||
public function stop():FlxSound {
|
||||
cleanup(autoDestroy, true);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function that tweens this sound's volume.
|
||||
*
|
||||
* @param duration The amount of time the fade-out operation should take.
|
||||
* @param to The volume to tween to, 0 by default.
|
||||
* @return This FlxSound instance (nice for chaining stuff together, if you're into that).
|
||||
*/
|
||||
public function fadeOut(duration = 1.0, to = 0.0, ?onComplete:FlxTween->Void):FlxSound {
|
||||
if (fadeTween != null) fadeTween.cancel();
|
||||
fadeTween = FlxTween.num(volume, to, duration, {onComplete: onComplete}, volumeTween);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function that tweens this sound's volume.
|
||||
* If the sound wasn't playing at all, it'll play before the tween starts.
|
||||
*
|
||||
* @param duration The amount of time the fade-in operation should take.
|
||||
* @param from The volume to tween from, 0 by default.
|
||||
* @param to The volume to tween to, 1 by default.
|
||||
* @return This FlxSound instance (nice for chaining stuff together, if you're into that).
|
||||
*/
|
||||
public function fadeIn(duration = 1.0, from = 0.0, to = 1.0, ?onComplete:FlxTween->Void):FlxSound {
|
||||
if (!playing) play();
|
||||
if (fadeTween != null) fadeTween.cancel();
|
||||
fadeTween = FlxTween.num(from, to, duration, {onComplete: onComplete}, volumeTween);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
function volumeTween(f:Float) volume = f;
|
||||
|
||||
/**
|
||||
* Returns the currently selected "real" volume of the sound (takes fades and proximity into account).
|
||||
*
|
||||
* @return The adjusted volume of the sound.
|
||||
*/
|
||||
public inline function getActualVolume():Float
|
||||
return (group != null ? group.getVolume() : 1.0) * _volume * _volumeAdjust;
|
||||
|
||||
#if FLX_PITCH
|
||||
/**
|
||||
* Returns the currently selected "real" pitch of the sound.
|
||||
*
|
||||
* @return The adjusted pitch of the sound.
|
||||
*/
|
||||
public inline function getActualPitch():Float
|
||||
return _realPitch;
|
||||
#end
|
||||
|
||||
/**
|
||||
* Returns the actual time coming from the internal, can be used for detecting sync error.
|
||||
*
|
||||
* @return The actual time of the sound.
|
||||
*/
|
||||
public inline function getActualTime():Float {
|
||||
get_time();
|
||||
return _time;
|
||||
}
|
||||
|
||||
/**
|
||||
* An internal helper function used to attempt to start playing
|
||||
* the sound and populate the _channel variable.
|
||||
*/
|
||||
function startSound(startTime:Float) @:privateAccess {
|
||||
if (_sound == null) return;
|
||||
|
||||
_paused = false;
|
||||
_time = startTime;
|
||||
_lastTime = FlxG.game.getTicks();
|
||||
if (_channel == null || !_channel.__isValid || _source == null #if lime_cffi || _source.__backend.disposed #end)
|
||||
makeChannel();
|
||||
|
||||
if (_channel != null) {
|
||||
#if FLX_PITCH
|
||||
_timeScaleAdjust = timeScaleBased ? FlxG.timeScale : 1.0;
|
||||
pitch = _pitch;
|
||||
#end
|
||||
|
||||
updateTransform();
|
||||
_channel.__lastPeakTime = -10;
|
||||
_channel.__leftPeak = 0;
|
||||
_channel.__rightPeak = 0;
|
||||
|
||||
#if lime_cffi _source.__backend.playing = true; #end
|
||||
_source.offset = 0;
|
||||
_source.currentTime = startTime + _offset;
|
||||
#if !lime_cffi _source.play(); #end
|
||||
|
||||
looped = looped;
|
||||
loopTime = loopTime;
|
||||
endTime = endTime;
|
||||
|
||||
active = true;
|
||||
}
|
||||
else {
|
||||
exists = false;
|
||||
active = false;
|
||||
}
|
||||
}
|
||||
|
||||
function stopped() {
|
||||
onFinish.dispatch();
|
||||
|
||||
if (onComplete != null) onComplete();
|
||||
|
||||
if (looped) {
|
||||
cleanup(false);
|
||||
play(false, loopTime, endTime);
|
||||
}
|
||||
else cleanup(autoDestroy);
|
||||
}
|
||||
|
||||
function source_looped() {
|
||||
if (onComplete != null) onComplete();
|
||||
|
||||
if (!looped) {
|
||||
cleanup(autoDestroy);
|
||||
_lastTime = FlxG.game.getTicks();
|
||||
_time = loopTime;
|
||||
}
|
||||
else if (_source != null)
|
||||
_source.loops = 999;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal event handler for ID3 info (i.e. fetching the song name).
|
||||
*/
|
||||
function gotID3(_) {
|
||||
name = _sound.id3.songName;
|
||||
artist = _sound.id3.artist;
|
||||
_sound.removeEventListener(Event.ID3, gotID3);
|
||||
}
|
||||
|
||||
#if FLX_SOUND_SYSTEM
|
||||
@:allow(flixel.system.frontEnds.SoundFrontEnd)
|
||||
function onFocus() if (!_alreadyPaused) {
|
||||
resume();
|
||||
_alreadyPaused = null;
|
||||
}
|
||||
|
||||
@:allow(flixel.system.frontEnds.SoundFrontEnd)
|
||||
function onFocusLost() if (_alreadyPaused == null && !(_alreadyPaused = _paused)) pause();
|
||||
#end
|
||||
|
||||
function set_group(value:FlxSoundGroup):FlxSoundGroup {
|
||||
if (value != null) value.add(this);
|
||||
else group.remove(this);
|
||||
return group;
|
||||
}
|
||||
|
||||
inline function get_playing():Bool @:privateAccess
|
||||
return _source != null && _source.playing;
|
||||
|
||||
inline function get_volume():Float
|
||||
return _volume;
|
||||
|
||||
inline function set_volume(v:Float):Float {
|
||||
_volume = FlxMath.bound(v, 0, 10);
|
||||
updateTransform();
|
||||
return _volume;
|
||||
}
|
||||
|
||||
inline function set_muted(v:Bool):Bool {
|
||||
muted = v;
|
||||
updateTransform();
|
||||
return muted;
|
||||
}
|
||||
|
||||
inline function get_pan():Float return _pan;
|
||||
inline function set_pan(v:Float):Float {
|
||||
_pan = FlxMath.bound(v, -1, 1);
|
||||
updateTransform();
|
||||
return _pan;
|
||||
}
|
||||
|
||||
inline function get_loaded():Bool
|
||||
return buffer != null;
|
||||
|
||||
inline function get_streamed():Bool
|
||||
@:privateAccess return #if lime_vorbis _sound != null && _sound.__buffer.__srcVorbisFile != null #else false #end;
|
||||
|
||||
inline function get_buffer():AudioBuffer
|
||||
@:privateAccess return _sound != null ? _sound.__buffer : null;
|
||||
|
||||
inline function update_amplitude():Void @:privateAccess {
|
||||
if (_channel != null && _channel.__updatePeaks(get_time()) && _amplitudeUpdate) {
|
||||
_amplitudeUpdate = false;
|
||||
_amplitudeLeft = _channel.__leftPeak;
|
||||
_amplitudeRight = _channel.__rightPeak;
|
||||
}
|
||||
}
|
||||
|
||||
inline function get_amplitudeLeft():Float {
|
||||
update_amplitude();
|
||||
return _amplitudeLeft;
|
||||
}
|
||||
|
||||
inline function get_amplitudeRight():Float {
|
||||
update_amplitude();
|
||||
return _amplitudeRight;
|
||||
}
|
||||
|
||||
inline function get_amplitude():Float {
|
||||
update_amplitude();
|
||||
return Math.max(_amplitudeLeft, _amplitudeRight);//if (stereo) (_amplitudeLeft + _amplitudeRight) * 0.5; else _amplitudeLeft;
|
||||
}
|
||||
|
||||
inline function get_channels():Int
|
||||
@:privateAccess return (buffer != null) ? buffer.channels : 0;
|
||||
|
||||
inline function get_stereo():Bool
|
||||
return channels > 1;
|
||||
|
||||
#if FLX_PITCH
|
||||
inline function get_pitch():Float
|
||||
return _pitch;
|
||||
|
||||
inline function set_pitch(v:Float):Float {
|
||||
_realPitch = (_pitch = v) * _timeScaleAdjust;
|
||||
if (_source != null) _source.pitch = _realPitch;
|
||||
return _pitch;
|
||||
}
|
||||
#end
|
||||
|
||||
function set_looped(v:Bool):Bool {
|
||||
if (playing) _source.loops = v ? 999 : 0;
|
||||
return looped = v;
|
||||
}
|
||||
|
||||
function set_loopTime(v:Float):Float {
|
||||
if (playing) _source.loopTime = v;
|
||||
return loopTime = v;
|
||||
}
|
||||
|
||||
function set_endTime(v:Null<Float>):Null<Float> {
|
||||
if (playing) {
|
||||
if (v != null && v > 0 && v < _length) _source.length = v;
|
||||
else _source.length = null;
|
||||
}
|
||||
return endTime = v;
|
||||
}
|
||||
|
||||
inline function getFakeTime():Float {
|
||||
if (_source.playing && _realPitch > 0 && _lastTime != null)
|
||||
return _time + (FlxG.game.getTicks() - _lastTime) * _realPitch * _timeInterpolation;
|
||||
else
|
||||
return _time;
|
||||
}
|
||||
function get_time():Float {
|
||||
if (_source == null || /*AudioManager.context == null*/funkin.backend.system.Main.audioDisconnected) return _time;
|
||||
|
||||
final sourceTime = _source.currentTime - _source.offset - _offset;
|
||||
if (!_source.playing || _realPitch <= 0) {
|
||||
_lastTime = null;
|
||||
return _time = sourceTime;
|
||||
}
|
||||
|
||||
final fakeTime = getFakeTime();
|
||||
if (sourceTime != _time) {
|
||||
_lastTime = FlxG.game.getTicks();
|
||||
if ((_timeInterpolation = 1 - Math.min(fakeTime - sourceTime, 1000) * 0.001) < 1 && _timeInterpolation > .9)
|
||||
return _time = fakeTime;
|
||||
else {
|
||||
_timeInterpolation = 1;
|
||||
return _time = sourceTime;
|
||||
}
|
||||
}
|
||||
else
|
||||
return fakeTime;
|
||||
}
|
||||
|
||||
function set_time(time:Float):Float @:privateAccess {
|
||||
time = FlxMath.bound(time, _offset, length - 1);
|
||||
if (_channel != null && _realPitch > 0) {
|
||||
if (!_channel.__isValid) {
|
||||
cleanup(false, true);
|
||||
startSound(time);
|
||||
}
|
||||
else {
|
||||
_source.offset = 0;
|
||||
_source.currentTime = time + _offset;
|
||||
}
|
||||
}
|
||||
|
||||
_lastTime = null;
|
||||
return _time = time;
|
||||
}
|
||||
|
||||
function get_offset():Float return _offset;
|
||||
function set_offset(offset:Float):Float {
|
||||
if (_offset == (_offset = offset)) return offset;
|
||||
//time = time + _offset;
|
||||
return offset;
|
||||
}
|
||||
|
||||
function get_length():Float return _length - _offset;
|
||||
|
||||
//function get_latency():Float return _source != null ? _source.latency : 0;
|
||||
|
||||
override function toString():String {
|
||||
return FlxStringUtil.getDebugString([
|
||||
LabelValuePair.weak("playing", playing),
|
||||
LabelValuePair.weak("time", time),
|
||||
LabelValuePair.weak("length", length),
|
||||
LabelValuePair.weak("volume", volume),
|
||||
LabelValuePair.weak("pitch", pitch)
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -1,120 +0,0 @@
|
||||
package flixel.sound;
|
||||
|
||||
/**
|
||||
* A way of grouping sounds for things such as collective volume control
|
||||
*/
|
||||
class FlxSoundGroup
|
||||
{
|
||||
/**
|
||||
* The sounds in this group
|
||||
*/
|
||||
public var sounds:Array<FlxSound> = [];
|
||||
|
||||
/**
|
||||
* The volume of this group
|
||||
*/
|
||||
public var volume(default, set):Float;
|
||||
|
||||
/**
|
||||
* Whether or not this group is muted
|
||||
*/
|
||||
public var muted(default, set):Bool;
|
||||
|
||||
/**
|
||||
* Create a new sound group
|
||||
* @param volume The initial volume of this group
|
||||
*/
|
||||
public function new(volume:Float = 1)
|
||||
{
|
||||
this.volume = volume;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a sound to this group, will remove the sound from any group it is currently in
|
||||
* @param sound The sound to add to this group
|
||||
* @return True if sound was successfully added, false otherwise
|
||||
*/
|
||||
public function add(sound:FlxSound):Bool
|
||||
{
|
||||
if (!sounds.contains(sound))
|
||||
{
|
||||
// remove from prev group
|
||||
if (sound.group != null)
|
||||
sound.group.sounds.remove(sound);
|
||||
|
||||
sounds.push(sound);
|
||||
@:bypassAccessor
|
||||
sound.group = this;
|
||||
sound.updateTransform();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a sound from this group
|
||||
* @param sound The sound to remove
|
||||
* @return True if sound was successfully removed, false otherwise
|
||||
*/
|
||||
public function remove(sound:FlxSound):Bool
|
||||
{
|
||||
if (sounds.contains(sound))
|
||||
{
|
||||
@:bypassAccessor
|
||||
sound.group = null;
|
||||
sounds.remove(sound);
|
||||
sound.updateTransform();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this function to pause all sounds in this group.
|
||||
* @since 4.3.0
|
||||
*/
|
||||
public function pause():Void
|
||||
{
|
||||
for (sound in sounds)
|
||||
sound.pause();
|
||||
}
|
||||
|
||||
/**
|
||||
* Unpauses all sounds in this group. Only works on sounds that have been paused.
|
||||
* @since 4.3.0
|
||||
*/
|
||||
public function resume():Void
|
||||
{
|
||||
for (sound in sounds)
|
||||
sound.resume();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the volume of this group, taking `muted` in account.
|
||||
* @return The volume of the group or 0 if the group is muted.
|
||||
*/
|
||||
public function getVolume():Float
|
||||
{
|
||||
return muted ? 0.0 : volume;
|
||||
}
|
||||
|
||||
function set_volume(volume:Float):Float
|
||||
{
|
||||
this.volume = volume;
|
||||
for (sound in sounds)
|
||||
{
|
||||
sound.updateTransform();
|
||||
}
|
||||
return volume;
|
||||
}
|
||||
|
||||
function set_muted(value:Bool):Bool
|
||||
{
|
||||
muted = value;
|
||||
for (sound in sounds)
|
||||
{
|
||||
sound.updateTransform();
|
||||
}
|
||||
return muted;
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,6 @@ import lime.utils.AssetLibrary;
|
||||
import haxe.ds.Map;
|
||||
|
||||
class AssetsLibraryList extends AssetLibrary {
|
||||
|
||||
public var libraries:Array<AssetLibrary> = [];
|
||||
public var cleanLibraries(get, never):Array<AssetLibrary>;
|
||||
function get_cleanLibraries():Array<AssetLibrary> {
|
||||
@@ -31,6 +30,9 @@ class AssetsLibraryList extends AssetLibrary {
|
||||
public var transLib:TranslatedAssetLibrary;
|
||||
#end
|
||||
|
||||
private var cachePaths:Map<String, AssetLibrary> = [];
|
||||
private var cacheNonExistentPaths:Map<String, Int> = [];
|
||||
|
||||
public function removeLibrary(lib:AssetLibrary) {
|
||||
if (lib != null) {
|
||||
libraries.remove(lib);
|
||||
@@ -54,12 +56,29 @@ class AssetsLibraryList extends AssetLibrary {
|
||||
public function existsSpecific(id:String, type:String, source:AssetSource = BOTH) {
|
||||
if (!id.startsWith("assets/") && existsSpecific('assets/$id', type, source))
|
||||
return true;
|
||||
|
||||
// There's a mod that heavily relies on addons and that can causes lags just to get a path.
|
||||
final sec = Date.now().getSeconds();
|
||||
if (cacheNonExistentPaths.exists(id) && sec - 10 < cacheNonExistentPaths.get(id)) {
|
||||
return false;
|
||||
}
|
||||
else if (cachePaths.exists(id)) {
|
||||
if (cachePaths.get(id).exists(id, type)) return true;
|
||||
else cachePaths.remove(id);
|
||||
}
|
||||
|
||||
|
||||
for(k=>l in libraries) {
|
||||
if (shouldSkipLib(l, source)) continue;
|
||||
if (l.exists(id, type)) {
|
||||
//trace("EXISTS", id, type, source);
|
||||
cachePaths.set(id, l);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
//trace("DOESNT EXISTS", id, type, source);
|
||||
cacheNonExistentPaths.set(id, sec);
|
||||
return false;
|
||||
}
|
||||
public override inline function exists(id:String, type:String):Bool
|
||||
@@ -198,6 +217,7 @@ class AssetsLibraryList extends AssetLibrary {
|
||||
public function reset() {
|
||||
unloadLibraries();
|
||||
|
||||
cachePaths.clear();
|
||||
libraries = [];
|
||||
|
||||
// adds default libraries in again
|
||||
|
||||
@@ -47,7 +47,7 @@ class MultiFramesCollection extends FlxFramesCollection
|
||||
public function addFrames(collection:FlxFramesCollection) {
|
||||
if (collection == null || collection.frames == null) return;
|
||||
|
||||
collection.parent.useCount++;
|
||||
collection.parent.incrementUseCount();
|
||||
parentedFrames.push(collection);
|
||||
|
||||
for(f in collection.frames) {
|
||||
@@ -63,7 +63,7 @@ class MultiFramesCollection extends FlxFramesCollection
|
||||
if(parentedFrames != null) {
|
||||
for(collection in parentedFrames) {
|
||||
if(collection != null)
|
||||
collection.parent.useCount--;
|
||||
collection.parent.decrementUseCount();
|
||||
}
|
||||
parentedFrames = null;
|
||||
}
|
||||
|
||||
@@ -194,7 +194,7 @@ class Paths
|
||||
return FlxAtlasFrames.fromAseprite('$key.${ext != null ? ext : Flags.IMAGE_EXT}', '$key.json');
|
||||
|
||||
inline static public function getAssetsRoot():String
|
||||
return ModsFolder.currentModFolder != null ? '${ModsFolder.modsPath}${ModsFolder.currentModFolder}' : #if (sys && TEST_BUILD) './${Main.pathBack}assets/' #else './assets' #end;
|
||||
return ModsFolder.currentModFolder != null ? '${ModsFolder.modsPath}${ModsFolder.currentModFolder}' : #if (sys && TEST_BUILD) './${Main.pathBack}assets/' #else './assets' #end;
|
||||
|
||||
/**
|
||||
* Gets frames at specified path.
|
||||
|
||||
@@ -89,7 +89,7 @@ class Script extends FlxBasic implements IFlxDestroyable {
|
||||
"Paths" => funkin.backend.assets.Paths,
|
||||
"Conductor" => funkin.backend.system.Conductor,
|
||||
"FunkinShader" => funkin.backend.shaders.FunkinShader,
|
||||
"CustomShader" => funkin.backend.shaders.CustomShader,
|
||||
"CustomShader" => funkin.backend.shaders.CustomShader, // deprecated
|
||||
"FunkinText" => funkin.backend.FunkinText,
|
||||
"FlxAnimate" => animate.FlxAnimate,
|
||||
"FunkinSprite" => funkin.backend.FunkinSprite,
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
package funkin.backend.shaders;
|
||||
|
||||
import flixel.util.FlxColor;
|
||||
import openfl.display.ShaderParameter;
|
||||
|
||||
@:dox(hide)
|
||||
typedef BlendModeShader =
|
||||
{
|
||||
var uBlendColor:ShaderParameter<Float>;
|
||||
}
|
||||
|
||||
@:dox(hide)
|
||||
class BlendModeEffect
|
||||
{
|
||||
public var shader(default, null):BlendModeShader;
|
||||
|
||||
@:isVar
|
||||
public var color(default, set):FlxColor;
|
||||
|
||||
public function new(shader:BlendModeShader, color:FlxColor):Void
|
||||
{
|
||||
shader.uBlendColor.value = [];
|
||||
this.shader = shader;
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
function set_color(color:FlxColor):FlxColor
|
||||
{
|
||||
shader.uBlendColor.value[0] = color.redFloat;
|
||||
shader.uBlendColor.value[1] = color.greenFloat;
|
||||
shader.uBlendColor.value[2] = color.blueFloat;
|
||||
shader.uBlendColor.value[3] = color.alphaFloat;
|
||||
|
||||
return this.color = color;
|
||||
}
|
||||
}
|
||||
@@ -2,39 +2,15 @@ package funkin.backend.shaders;
|
||||
|
||||
import openfl.Assets;
|
||||
|
||||
/**
|
||||
* Class for custom shaders.
|
||||
*
|
||||
* To create one, create a `shaders` folder in your assets/mod folder, then add a file named `my-shader.frag` or/and `my-shader.vert`.
|
||||
*
|
||||
* Non-existent shaders will only load the default one, and throw a warning in the console.
|
||||
*
|
||||
* To access the shader's uniform variables, use `shader.variable`
|
||||
*/
|
||||
@:deprecated("Use funkin.backend.shaders.FunkinShader.fromFile instead.")
|
||||
class CustomShader extends FunkinShader {
|
||||
public var path:String = "";
|
||||
@:isVar
|
||||
public var path(get, set):String;
|
||||
inline function get_path():String return path != null ? path : _fragmentFilePath + _vertexFilePath;
|
||||
inline function set_path(v:Null<String>):String return path = cast v;
|
||||
|
||||
/**
|
||||
* Creates a new custom shader
|
||||
* @param name Name of the frag and vert files.
|
||||
* @param glslVersion GLSL version to use. Defaults to `120`.
|
||||
*/
|
||||
public function new(name:String, glslVersion:String = null) {
|
||||
if (glslVersion == null) glslVersion = Flags.DEFAULT_GLSL_VERSION;
|
||||
var fragShaderPath = Paths.fragShader(name);
|
||||
var vertShaderPath = Paths.vertShader(name);
|
||||
var fragCode = Assets.exists(fragShaderPath) ? Assets.getText(fragShaderPath) : null;
|
||||
var vertCode = Assets.exists(vertShaderPath) ? Assets.getText(vertShaderPath) : null;
|
||||
|
||||
fileName = name;
|
||||
fragFileName = fragShaderPath;
|
||||
vertFileName = vertShaderPath;
|
||||
|
||||
path = fragShaderPath+vertShaderPath;
|
||||
|
||||
if (fragCode == null && vertCode == null)
|
||||
Logs.error('Shader "$name" couldn\'t be found.');
|
||||
|
||||
super(fragCode, vertCode, glslVersion);
|
||||
public function new(name:String, ?glslVersion:String) {
|
||||
super();
|
||||
loadShaderFile(Paths.fragShader(name), Paths.vertShader(name), glslVersion);
|
||||
}
|
||||
}
|
||||
@@ -1,533 +1,65 @@
|
||||
package funkin.backend.shaders;
|
||||
|
||||
import flixel.graphics.FlxGraphic;
|
||||
import flixel.system.FlxAssets.FlxShader;
|
||||
import flixel.util.FlxSignal.FlxTypedSignal;
|
||||
import haxe.io.Path;
|
||||
import haxe.Exception;
|
||||
|
||||
import hscript.IHScriptCustomBehaviour;
|
||||
import lime.utils.Float32Array;
|
||||
|
||||
import openfl.display.BitmapData;
|
||||
import openfl.display.ShaderInput;
|
||||
import openfl.display.Shader;
|
||||
import openfl.display.ShaderParameter;
|
||||
import openfl.display.ShaderParameterType;
|
||||
import openfl.display.ShaderPrecision;
|
||||
import openfl.display.ShaderInput;
|
||||
import openfl.display3D._internal.GLProgram;
|
||||
import openfl.display3D._internal.GLShader;
|
||||
import openfl.display3D.Program3D;
|
||||
import openfl.utils._internal.Log;
|
||||
import openfl.utils.GLSLSourceAssembler;
|
||||
|
||||
import flixel.addons.display.FlxRuntimeShader;
|
||||
import flixel.graphics.FlxGraphic;
|
||||
import flixel.util.FlxSignal.FlxTypedSignal;
|
||||
import flixel.util.FlxStringUtil;
|
||||
|
||||
using StringTools;
|
||||
@:access(openfl.display3D.Context3D)
|
||||
@:access(openfl.display3D.Program3D)
|
||||
@:access(openfl.display.ShaderInput)
|
||||
@:access(openfl.display.ShaderParameter)
|
||||
class FunkinShader extends FlxShader implements IHScriptCustomBehaviour {
|
||||
private static var __instanceFields = Type.getInstanceFields(FunkinShader);
|
||||
private static var FRAGMENT_SHADER = 0;
|
||||
private static var VERTEX_SHADER = 1;
|
||||
|
||||
class FunkinShader extends FlxRuntimeShader implements IHScriptCustomBehaviour {
|
||||
public var onGLUpdate:FlxTypedSignal<Void->Void> = new FlxTypedSignal<Void->Void>();
|
||||
public var onProcessGLData:FlxTypedSignal<(String, String)->Void> = new FlxTypedSignal<(String, String)->Void>();
|
||||
|
||||
public var glslVer:String = Flags.DEFAULT_GLSL_VERSION;
|
||||
public var fileName:String = "FunkinShader";
|
||||
public var fragFileName:String = "FunkinShader";
|
||||
public var vertFileName:String = "FunkinShader";
|
||||
|
||||
public var shaderPrefix:String = "";
|
||||
public var fragmentPrefix:String = "";
|
||||
public var vertexPrefix:String = "";
|
||||
|
||||
/**
|
||||
* Creates a new shader from the specified fragment and vertex source.
|
||||
* Accepts `#pragma header`.
|
||||
* @param frag Fragment source (pass `null` to use default)
|
||||
* @param vert Vertex source (pass `null` to use default)
|
||||
* @param glslVer Version of GLSL to use (defaults to 120)
|
||||
*/
|
||||
public override function new(frag:String, vert:String, glslVer:String = null) {
|
||||
if (glslVer == null) glslVer = Flags.DEFAULT_GLSL_VERSION;
|
||||
if (frag == null) frag = ShaderTemplates.defaultFragmentSource;
|
||||
if (vert == null) vert = ShaderTemplates.defaultVertexSource;
|
||||
this.glFragmentSource = frag;
|
||||
this.glVertexSource = vert;
|
||||
|
||||
this.glslVer = glslVer;
|
||||
super();
|
||||
public function new(?fragmentSource:String, ?vertexSource:String, ?version:String) {
|
||||
super(fragmentSource, vertexSource, version ?? (fragmentSource != null || vertexSource != null ? Flags.DEFAULT_GLSL_VERSION : null));
|
||||
}
|
||||
|
||||
static var IMPORT_REGEX = ~/#import\s+<(.*)>/;
|
||||
|
||||
private function processImports(value:String, type:Int):String
|
||||
{
|
||||
while(IMPORT_REGEX.match(value))
|
||||
{
|
||||
var importPath = IMPORT_REGEX.matched(1);
|
||||
var importSource = Assets.getText("assets/shaders/" + importPath);
|
||||
if(importSource == null) {
|
||||
var fileName = type == FRAGMENT_SHADER ? fragFileName : vertFileName;
|
||||
Logs.traceColored([
|
||||
Logs.logText('[Shader] ', RED),
|
||||
Logs.logText('Failed to import shader ${importPath} in ${fileName}', RED),
|
||||
]);
|
||||
} else {
|
||||
value = value.replace(IMPORT_REGEX.matched(0), importSource);
|
||||
}
|
||||
}
|
||||
return value;
|
||||
public static function fromFile(fragmentPath:String, ?vertexPath:String, ?version:String):FunkinShader {
|
||||
return new FunkinShader().loadShaderFile(fragmentPath, vertexPath, version);
|
||||
}
|
||||
|
||||
static var ERROR_POS_REGEX = ~/(\d+):(\d+): (.*)/g;
|
||||
static var ERROR_REGEX = ~/ERROR: (\d+):(\d+): (.*)/g;
|
||||
static var ERROR_REGEX_2 = ~/(\d+)\((\d+)\) : error ([^:]+): (.*)/g;
|
||||
@:noCompletion private override function __createGLShader(source:String, type:Int):GLShader
|
||||
{
|
||||
var gl = __context.gl;
|
||||
|
||||
var shader = gl.createShader(type);
|
||||
gl.shaderSource(shader, source);
|
||||
gl.compileShader(shader);
|
||||
var shaderInfoLog = gl.getShaderInfoLog(shader);
|
||||
var hasInfoLog = shaderInfoLog != null && StringTools.trim(shaderInfoLog) != "";
|
||||
var compileStatus = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
|
||||
|
||||
if (hasInfoLog || compileStatus == 0)
|
||||
{
|
||||
var isVertexShader = type == gl.VERTEX_SHADER;
|
||||
var messageBuf = new StringBuf();
|
||||
messageBuf.add((compileStatus == 0) ? "Error" : "Info");
|
||||
if(isVertexShader) {
|
||||
messageBuf.add(" compiling vertex shader");
|
||||
if(vertFileName != null && vertFileName.length > 0) {
|
||||
messageBuf.add(" (" + vertFileName + ")");
|
||||
}
|
||||
} else {
|
||||
messageBuf.add(" compiling fragment shader");
|
||||
if(fragFileName != null && fragFileName.length > 0) {
|
||||
messageBuf.add(" (" + fragFileName + ")");
|
||||
}
|
||||
}
|
||||
messageBuf.add("\n");
|
||||
var errorPositions = [];
|
||||
var regex = null;
|
||||
var tmp = shaderInfoLog;
|
||||
if(shaderInfoLog.contains(" : error ")) {
|
||||
regex = ERROR_REGEX_2;
|
||||
|
||||
while(regex.match(tmp)) {
|
||||
errorPositions.push(new ShaderErrorPosition(regex.matched(2), regex.matched(1), regex.matched(4)));
|
||||
tmp = regex.matchedRight();
|
||||
}
|
||||
} else if(shaderInfoLog.contains("ERROR: ")) {
|
||||
regex = ERROR_REGEX;
|
||||
|
||||
while(regex.match(tmp)) {
|
||||
errorPositions.push(new ShaderErrorPosition(regex.matched(2), regex.matched(1), regex.matched(3)));
|
||||
tmp = regex.matchedRight();
|
||||
}
|
||||
} else {
|
||||
regex = ERROR_POS_REGEX;
|
||||
|
||||
while(regex.match(tmp)) {
|
||||
errorPositions.push(new ShaderErrorPosition(regex.matched(2), regex.matched(1), regex.matched(3)));
|
||||
tmp = regex.matchedRight();
|
||||
}
|
||||
}
|
||||
var splitSource = source.split("\n");
|
||||
for(error in errorPositions) {
|
||||
messageBuf.add("ERROR: Line: " + error.line);
|
||||
if(error.column > 0) {
|
||||
messageBuf.add(", Column: " + error.column);
|
||||
}
|
||||
messageBuf.add(", " + error.message);
|
||||
if(error.line < splitSource.length) {
|
||||
messageBuf.add("\nLine: ");
|
||||
messageBuf.add(splitSource[error.line-1].trim());
|
||||
}
|
||||
messageBuf.add("\n\n");
|
||||
}
|
||||
var hasErrorPosition = errorPositions.length > 0;
|
||||
if(hasErrorPosition) {
|
||||
messageBuf.add("Raw shader info log:\n");
|
||||
}
|
||||
messageBuf.add(shaderInfoLog);
|
||||
messageBuf.add("\n");
|
||||
messageBuf.add(source);
|
||||
|
||||
var message = messageBuf.toString();
|
||||
if (compileStatus == 0) Log.error(message);
|
||||
else if (hasInfoLog) Log.debug(message);
|
||||
public function loadShaderFile(fragmentPath:String, ?vertexPath:String, ?version:String):FunkinShader {
|
||||
if (vertexPath == null) {
|
||||
final idx = fragmentPath.lastIndexOf(".");
|
||||
if (idx == -1) vertexPath = fragmentPath;
|
||||
else vertexPath = fragmentPath.substr(0, idx);
|
||||
}
|
||||
|
||||
return shader;
|
||||
}
|
||||
fragmentPath = FlxRuntimeShader._getPath(fragmentPath, false);
|
||||
vertexPath = FlxRuntimeShader._getPath(vertexPath, true);
|
||||
_fromFile(fragmentPath, vertexPath, version ?? (fragmentPath != null || vertexPath != null ? Flags.DEFAULT_GLSL_VERSION : null));
|
||||
|
||||
@:noCompletion private override function __createGLProgram(vertexSource:String, fragmentSource:String):GLProgram
|
||||
{
|
||||
var program:GLProgram = null;
|
||||
try
|
||||
{
|
||||
var gl = __context.gl;
|
||||
|
||||
var vertexShader = __createGLShader(vertexSource, gl.VERTEX_SHADER);
|
||||
var fragmentShader = __createGLShader(fragmentSource, gl.FRAGMENT_SHADER);
|
||||
|
||||
program = gl.createProgram();
|
||||
|
||||
// Fix support for drivers that don't draw if attribute 0 is disabled
|
||||
for (param in __paramFloat)
|
||||
{
|
||||
if (param.name.indexOf("Position") > -1 && StringTools.startsWith(param.name, "openfl_"))
|
||||
{
|
||||
gl.bindAttribLocation(program, 0, param.name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
gl.attachShader(program, vertexShader);
|
||||
gl.attachShader(program, fragmentShader);
|
||||
gl.linkProgram(program);
|
||||
|
||||
if (gl.getProgramParameter(program, gl.LINK_STATUS) == 0)
|
||||
{
|
||||
var messageBuf = new StringBuf();
|
||||
messageBuf.add("Unable to initialize the shader program");
|
||||
messageBuf.add("\n");
|
||||
messageBuf.add(gl.getProgramInfoLog(program));
|
||||
var message = messageBuf.toString();
|
||||
Log.error(message);
|
||||
}
|
||||
}
|
||||
catch (error:Dynamic)
|
||||
{
|
||||
Logs.traceColored([
|
||||
Logs.logText('[Shader] ', BLUE),
|
||||
Logs.logText('Failed to compile shader ${fileName}: ', RED),
|
||||
Logs.logText(Std.string(error))
|
||||
], TRACE);
|
||||
}
|
||||
return program;
|
||||
|
||||
return program;
|
||||
}
|
||||
|
||||
var glRawFragmentSource:String;
|
||||
var glRawVertexSource:String;
|
||||
|
||||
@:noCompletion override private function set_glFragmentSource(value:String):String
|
||||
{
|
||||
if(value == null)
|
||||
value = ShaderTemplates.defaultFragmentSource;
|
||||
glRawFragmentSource = value;
|
||||
value = processImports(value, FRAGMENT_SHADER);
|
||||
value = value.replace("#pragma header", ShaderTemplates.fragHeader).replace("#pragma body", ShaderTemplates.fragBody);
|
||||
if (value != __glFragmentSource)
|
||||
{
|
||||
__glSourceDirty = true;
|
||||
}
|
||||
|
||||
return __glFragmentSource = value;
|
||||
}
|
||||
|
||||
@:noCompletion override private function set_glVertexSource(value:String):String
|
||||
{
|
||||
if(value == null)
|
||||
value = ShaderTemplates.defaultVertexSource;
|
||||
glRawVertexSource = value;
|
||||
value = processImports(value, VERTEX_SHADER);
|
||||
|
||||
var useBackCompat:Bool = true;
|
||||
for (regex in ShaderTemplates.vertBackCompatVarList) if (!regex.match(value)) {
|
||||
useBackCompat = false;
|
||||
break;
|
||||
}
|
||||
|
||||
var header = useBackCompat ? ShaderTemplates.vertHeaderBackCompat : ShaderTemplates.vertHeader;
|
||||
var body = useBackCompat ? ShaderTemplates.vertBodyBackCompat : ShaderTemplates.vertBody;
|
||||
|
||||
value = value.replace("#pragma header", header).replace("#pragma body", body);
|
||||
|
||||
if (value != __glVertexSource)
|
||||
{
|
||||
__glSourceDirty = true;
|
||||
}
|
||||
|
||||
return __glVertexSource = value;
|
||||
}
|
||||
|
||||
override function __updateGL():Void {
|
||||
onGLUpdate.dispatch();
|
||||
super.__updateGL();
|
||||
}
|
||||
|
||||
@:noCompletion private override function __initGL():Void
|
||||
{
|
||||
if (__glSourceDirty || __paramBool == null)
|
||||
{
|
||||
__glSourceDirty = false;
|
||||
program = null;
|
||||
|
||||
__inputBitmapData = new Array();
|
||||
__paramBool = new Array();
|
||||
__paramFloat = new Array();
|
||||
__paramInt = new Array();
|
||||
|
||||
__processGLData(glVertexSource, "attribute");
|
||||
__processGLData(glVertexSource, "uniform");
|
||||
__processGLData(glFragmentSource, "uniform");
|
||||
}
|
||||
|
||||
if (__context != null && program == null)
|
||||
{
|
||||
var prefixBuf = new StringBuf();
|
||||
prefixBuf.add('#version ${glslVer}\n');
|
||||
prefixBuf.add(shaderPrefix);
|
||||
|
||||
var gl = __context.gl;
|
||||
|
||||
prefixBuf.add("#ifdef GL_ES\n");
|
||||
if (precisionHint == FULL) {
|
||||
prefixBuf.add("#ifdef GL_FRAGMENT_PRECISION_HIGH\n");
|
||||
prefixBuf.add("precision highp float;\n");
|
||||
prefixBuf.add("#else\n");
|
||||
prefixBuf.add("precision mediump float;\n");
|
||||
prefixBuf.add("#endif\n");
|
||||
} else {
|
||||
prefixBuf.add("precision lowp float;\n");
|
||||
}
|
||||
prefixBuf.add("#endif\n");
|
||||
|
||||
var prefix = prefixBuf.toString();
|
||||
|
||||
var vertex = prefix + vertexPrefix + glVertexSource;
|
||||
var fragment = prefix + fragmentPrefix + glFragmentSource;
|
||||
|
||||
var id = vertex + fragment;
|
||||
|
||||
if (__context.__programs.exists(id))
|
||||
{
|
||||
program = __context.__programs.get(id);
|
||||
}
|
||||
else
|
||||
{
|
||||
program = __context.createProgram(GLSL);
|
||||
program.__glProgram = __createGLProgram(vertex, fragment);
|
||||
__context.__programs.set(id, program);
|
||||
}
|
||||
|
||||
if (program != null)
|
||||
{
|
||||
glProgram = program.__glProgram;
|
||||
|
||||
for (input in __inputBitmapData) {
|
||||
|
||||
if (input.__isUniform) {
|
||||
input.index = gl.getUniformLocation(glProgram, input.name);
|
||||
} else {
|
||||
input.index = gl.getAttribLocation(glProgram, input.name);
|
||||
}
|
||||
}
|
||||
|
||||
for (parameter in __paramBool) {
|
||||
if (parameter.__isUniform) {
|
||||
parameter.index = gl.getUniformLocation(glProgram, parameter.name);
|
||||
} else {
|
||||
parameter.index = gl.getAttribLocation(glProgram, parameter.name);
|
||||
}
|
||||
}
|
||||
|
||||
for (parameter in __paramFloat) {
|
||||
if (parameter.__isUniform) {
|
||||
parameter.index = gl.getUniformLocation(glProgram, parameter.name);
|
||||
} else {
|
||||
parameter.index = gl.getAttribLocation(glProgram, parameter.name);
|
||||
}
|
||||
}
|
||||
|
||||
for (parameter in __paramInt) {
|
||||
if (parameter.__isUniform) {
|
||||
parameter.index = gl.getUniformLocation(glProgram, parameter.name);
|
||||
} else {
|
||||
parameter.index = gl.getAttribLocation(glProgram, parameter.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
// initInstance(vertex, fragment); // btw make sure to disable the prefixes for ._isInstance
|
||||
}
|
||||
}
|
||||
|
||||
var __cancelNextProcessGLData:Bool = false;
|
||||
@:noCompletion private override function __processGLData(source:String, storageType:String):Void
|
||||
{
|
||||
onProcessGLData.dispatch(source, storageType);
|
||||
if (__cancelNextProcessGLData != (__cancelNextProcessGLData = false))
|
||||
return;
|
||||
var lastMatch = 0, position, regex, name, type;
|
||||
|
||||
if (storageType == "uniform")
|
||||
{
|
||||
regex = ~/uniform ([A-Za-z0-9]+) ([A-Za-z0-9_]+)/;
|
||||
}
|
||||
else
|
||||
{
|
||||
regex = ~/attribute ([A-Za-z0-9]+) ([A-Za-z0-9_]+)/;
|
||||
}
|
||||
|
||||
while (regex.matchSub(source, lastMatch))
|
||||
{
|
||||
type = regex.matched(1);
|
||||
name = regex.matched(2);
|
||||
|
||||
if (StringTools.startsWith(name, "gl_"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var isUniform = (storageType == "uniform");
|
||||
registerParameter(name, type, isUniform);
|
||||
|
||||
position = regex.matchedPos();
|
||||
lastMatch = position.pos + position.len;
|
||||
}
|
||||
}
|
||||
|
||||
function registerParameter(name:String, type:String, isUniform:Bool):Void
|
||||
{
|
||||
if (StringTools.startsWith(type, "sampler"))
|
||||
{
|
||||
var input = new ShaderInput<BitmapData>();
|
||||
input.name = name;
|
||||
input.__isUniform = isUniform;
|
||||
__inputBitmapData.push(input);
|
||||
|
||||
switch (name)
|
||||
{
|
||||
case "openfl_Texture":
|
||||
__texture = input;
|
||||
case "bitmap":
|
||||
__bitmap = input;
|
||||
default:
|
||||
}
|
||||
|
||||
Reflect.setField(__data, name, input);
|
||||
try{Reflect.setField(this, name, input);} catch(e) {}
|
||||
}
|
||||
else if (!Reflect.hasField(__data, name) || Reflect.field(__data, name) == null)
|
||||
{
|
||||
var parameterType:ShaderParameterType = switch (type)
|
||||
{
|
||||
case "bool": BOOL;
|
||||
case "double", "float": FLOAT;
|
||||
case "int", "uint": INT;
|
||||
case "bvec2": BOOL2;
|
||||
case "bvec3": BOOL3;
|
||||
case "bvec4": BOOL4;
|
||||
case "ivec2", "uvec2": INT2;
|
||||
case "ivec3", "uvec3": INT3;
|
||||
case "ivec4", "uvec4": INT4;
|
||||
case "vec2", "dvec2": FLOAT2;
|
||||
case "vec3", "dvec3": FLOAT3;
|
||||
case "vec4", "dvec4": FLOAT4;
|
||||
case "mat2", "mat2x2": MATRIX2X2;
|
||||
case "mat2x3": MATRIX2X3;
|
||||
case "mat2x4": MATRIX2X4;
|
||||
case "mat3x2": MATRIX3X2;
|
||||
case "mat3", "mat3x3": MATRIX3X3;
|
||||
case "mat3x4": MATRIX3X4;
|
||||
case "mat4x2": MATRIX4X2;
|
||||
case "mat4x3": MATRIX4X3;
|
||||
case "mat4", "mat4x4": MATRIX4X4;
|
||||
default: null;
|
||||
}
|
||||
|
||||
var length = switch (parameterType)
|
||||
{
|
||||
case BOOL2, INT2, FLOAT2: 2;
|
||||
case BOOL3, INT3, FLOAT3: 3;
|
||||
case BOOL4, INT4, FLOAT4, MATRIX2X2: 4;
|
||||
case MATRIX3X3: 9;
|
||||
case MATRIX4X4: 16;
|
||||
default: 1;
|
||||
}
|
||||
|
||||
var arrayLength = switch (parameterType)
|
||||
{
|
||||
case MATRIX2X2: 2;
|
||||
case MATRIX3X3: 3;
|
||||
case MATRIX4X4: 4;
|
||||
default: 1;
|
||||
}
|
||||
|
||||
switch (parameterType)
|
||||
{
|
||||
case BOOL, BOOL2, BOOL3, BOOL4:
|
||||
var parameter = new ShaderParameter<Bool>();
|
||||
parameter.name = name;
|
||||
parameter.type = parameterType;
|
||||
parameter.__arrayLength = arrayLength;
|
||||
parameter.__isBool = true;
|
||||
parameter.__isUniform = isUniform;
|
||||
parameter.__length = length;
|
||||
__paramBool.push(parameter);
|
||||
|
||||
if (name == "openfl_HasColorTransform")
|
||||
{
|
||||
__hasColorTransform = parameter;
|
||||
}
|
||||
|
||||
Reflect.setField(__data, name, parameter);
|
||||
try{Reflect.setField(this, name, parameter);} catch(e) {}
|
||||
|
||||
case INT, INT2, INT3, INT4:
|
||||
var parameter = new ShaderParameter<Int>();
|
||||
parameter.name = name;
|
||||
parameter.type = parameterType;
|
||||
parameter.__arrayLength = arrayLength;
|
||||
parameter.__isInt = true;
|
||||
parameter.__isUniform = isUniform;
|
||||
parameter.__length = length;
|
||||
__paramInt.push(parameter);
|
||||
Reflect.setField(__data, name, parameter);
|
||||
try{Reflect.setField(this, name, parameter);} catch(e) {}
|
||||
|
||||
default:
|
||||
var parameter = new ShaderParameter<Float>();
|
||||
parameter.name = name;
|
||||
parameter.type = parameterType;
|
||||
parameter.__arrayLength = arrayLength;
|
||||
#if lime
|
||||
if (arrayLength > 0) parameter.__uniformMatrix = new Float32Array(arrayLength * arrayLength);
|
||||
#end
|
||||
parameter.__isFloat = true;
|
||||
parameter.__isUniform = isUniform;
|
||||
parameter.__length = length;
|
||||
__paramFloat.push(parameter);
|
||||
|
||||
if (StringTools.startsWith(name, "openfl_"))
|
||||
{
|
||||
switch (name)
|
||||
{
|
||||
case "openfl_Alpha": __alpha = parameter;
|
||||
case "openfl_ColorMultiplier": __colorMultiplier = parameter;
|
||||
case "openfl_ColorOffset": __colorOffset = parameter;
|
||||
case "openfl_Matrix": __matrix = parameter;
|
||||
case "openfl_Position": __position = parameter;
|
||||
case "openfl_TextureCoord": __textureCoord = parameter;
|
||||
case "openfl_TextureSize": __textureSize = parameter;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
Reflect.setField(__data, name, parameter);
|
||||
try{Reflect.setField(this, name, parameter);} catch(e) {}
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
#if REGION /* IHScriptCustomBehaviour */
|
||||
public function hget(name:String):Dynamic {
|
||||
if (__instanceFields.contains(name) || __instanceFields.contains('get_${name}'))
|
||||
return Reflect.getProperty(this, name);
|
||||
if (!Reflect.hasField(data, name))
|
||||
return null;
|
||||
var field:Dynamic = Reflect.field(data, name);
|
||||
if (__glSourceDirty) __init();
|
||||
|
||||
if (__thisHasField(name) || __thisHasField('get_${name}')) return Reflect.getProperty(this, name);
|
||||
else if (!Reflect.hasField(__data, name)) return null;
|
||||
|
||||
final field:Dynamic = Reflect.field(__data, name);
|
||||
|
||||
var cl:String = Type.getClassName(Type.getClass(field));
|
||||
|
||||
// little problem we are facing boys...
|
||||
@@ -546,17 +78,19 @@ class FunkinShader extends FlxShader implements IHScriptCustomBehaviour {
|
||||
}
|
||||
|
||||
public function hset(name:String, val:Dynamic):Dynamic {
|
||||
if (__instanceFields.contains(name) || __instanceFields.contains('set_${name}')) {
|
||||
if (__glSourceDirty) __init();
|
||||
|
||||
if (__thisHasField(name) || __thisHasField('set_${name}')) {
|
||||
Reflect.setProperty(this, name, val);
|
||||
return val;
|
||||
}
|
||||
|
||||
if (!Reflect.hasField(data, name)) {
|
||||
Reflect.setField(data, name, val);
|
||||
else if (!Reflect.hasField(__data, name)) {
|
||||
// ??? huh
|
||||
Reflect.setField(__data, name, val);
|
||||
return val;
|
||||
}
|
||||
|
||||
var field = Reflect.field(data, name);
|
||||
var field = Reflect.field(__data, name);
|
||||
var cl = Type.getClassName(Type.getClass(field));
|
||||
var isNotNull = val != null;
|
||||
// cant do "field is ShaderInput" for some reason
|
||||
@@ -598,67 +132,142 @@ class FunkinShader extends FlxShader implements IHScriptCustomBehaviour {
|
||||
|
||||
return val;
|
||||
}
|
||||
#end
|
||||
|
||||
override function __updateGL():Void {
|
||||
onGLUpdate.dispatch();
|
||||
super.__updateGL();
|
||||
}
|
||||
|
||||
override function __createAssembler():Void {
|
||||
__glSourceAssembler = new FunkinShaderSourceAssembler(this);
|
||||
}
|
||||
|
||||
override function toString():String {
|
||||
return __cacheProgramId != null ? 'FunkinShader(${__cacheProgramId})' : 'FunkinShader';
|
||||
}
|
||||
|
||||
#if REGION /* Deprecated */
|
||||
public var shaderPrefix:String = "";
|
||||
public var fragmentPrefix:String = "";
|
||||
public var vertexPrefix:String = "";
|
||||
#end
|
||||
|
||||
#if REGION /* Backward Compatibility */
|
||||
private static var __instanceFields = Type.getInstanceFields(FunkinShader);
|
||||
private static var FRAGMENT_SHADER = 0;
|
||||
private static var VERTEX_SHADER = 1;
|
||||
|
||||
public var fileName(get, set):String;
|
||||
inline function get_fileName():String return _fragmentFilePath ?? _vertexFilePath ?? "FunkinShader";
|
||||
inline function set_fileName(v:String):String return _fragmentFilePath = _vertexFilePath = v;
|
||||
|
||||
public var fragFileName(get, set):String;
|
||||
inline function get_fragFileName():String return _fragmentFilePath ?? "FunkinShader";
|
||||
inline function set_fragFileName(v:String):String return _fragmentFilePath = v;
|
||||
|
||||
public var vertFileName(get, set):String;
|
||||
inline function get_vertFileName():String return _vertexFilePath ?? "FunkinShader";
|
||||
inline function set_vertFileName(v:String):String return _vertexFilePath = v;
|
||||
|
||||
public var glslVer(get, set):String;
|
||||
inline function get_glslVer():String return glVersion;
|
||||
inline function set_glslVer(v:String):String return glVersion = v;
|
||||
|
||||
public var glRawFragmentSource(get, set):String;
|
||||
inline function get_glRawFragmentSource():String return __glFragmentSourceRaw;
|
||||
inline function set_glRawFragmentSource(v:String):String return __glFragmentSourceRaw = v;
|
||||
|
||||
public var glRawVertexSource(get, set):String;
|
||||
inline function get_glRawVertexSource():String return __glVertexSourceRaw;
|
||||
inline function set_glRawVertexSource(v:String):String return __glVertexSourceRaw = v;
|
||||
|
||||
function thisHasField(v:String):Bool return __thisHasField(v);
|
||||
|
||||
function registerParameter(name:String, type:String, isUniform:Bool) {
|
||||
__registerParameter(name, Shader.getParameterTypeFromGLSL(type, false), StringTools.startsWith(type, "sampler"), 1, null, isUniform, null);
|
||||
}
|
||||
|
||||
// Unused... cne-openfl uses a different system
|
||||
var __cancelNextProcessGLData:Bool = false;
|
||||
public var onProcessGLData:FlxTypedSignal<(String, String)->Void> = new FlxTypedSignal<(String, String)->Void>();
|
||||
#end
|
||||
}
|
||||
|
||||
class FunkinShaderSourceAssembler extends FlxRuntimeShader.FlxShaderSourceAssembler {
|
||||
final funkinParent:FunkinShader;
|
||||
|
||||
public function new(parent:FunkinShader) {
|
||||
super(funkinParent = parent);
|
||||
}
|
||||
|
||||
override function __appendIncludes(source:String, isVertex:Bool, ?includedKeys:Map<String, Bool>):String
|
||||
{
|
||||
if (includedKeys == null) includedKeys = [];
|
||||
|
||||
source = GLSLSourceAssembler.__getIncludeFinder().map(source, (regex:EReg) ->
|
||||
{
|
||||
var key = regex.matched(1);
|
||||
if (includedKeys.get(key)) return '/*Recursive include $key*/\n';
|
||||
|
||||
var include = __getIncludeSource(key, isVertex);
|
||||
if (include == null) return '/*Unknown include $key*/\n';
|
||||
|
||||
includedKeys.set(key, true);
|
||||
return '/*include $key*/\n' + __appendIncludes(include, isVertex, includedKeys);
|
||||
});
|
||||
|
||||
return __getImportCompatibilityFinder().map(source, (regex:EReg) ->
|
||||
{
|
||||
var key = regex.matched(1);
|
||||
if (includedKeys.get(key)) return '/*Recursive import $key*/\n';
|
||||
|
||||
var include = __getIncludeSource(key, isVertex);
|
||||
if (include == null) return '/*Unknown import $key*/\n';
|
||||
|
||||
includedKeys.set(key, true);
|
||||
return '/*import $key*/\n' + __appendIncludes(include, isVertex, includedKeys);
|
||||
});
|
||||
}
|
||||
|
||||
override function __getIncludeSource(include:String, fromVertex:Bool):Null<String> {
|
||||
final path = Paths.getPath('shaders/' + include);
|
||||
if (Assets.exists(path)) return Assets.getText(path);
|
||||
|
||||
final fallback = __getIncludeSource(include, fromVertex);
|
||||
if (fallback != null) return fallback;
|
||||
|
||||
Logs.traceColored([
|
||||
Logs.logText('[Shader] ', RED),
|
||||
Logs.logText('Failed to import shader $include', RED),
|
||||
]);
|
||||
return null;
|
||||
}
|
||||
|
||||
override function __appendPrefix(source:String, versionNumber:Int, versionProfile:String, extensions:Map<String, String>, isVertex:Bool,
|
||||
precisionHint:Null<ShaderPrecision>):String
|
||||
{
|
||||
var result = super.__appendPrefix(null, versionNumber, versionProfile, extensions, isVertex, precisionHint) + "\n";
|
||||
|
||||
result += funkinParent.shaderPrefix + "\n" + (isVertex ? funkinParent.vertexPrefix : funkinParent.fragmentPrefix) + "\n";
|
||||
|
||||
if (source != null) {
|
||||
if (!isVertex && versionNumber >= 300 && versionProfile != "compatibility" && !StringTools.contains(source, "out vec4")) {
|
||||
result += "out vec4 openfl_FragColor;\n";
|
||||
}
|
||||
result += source;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static inline function __getImportCompatibilityFinder():EReg {
|
||||
return ~/#import\s+(?|"([^"]+)"|'([^']+)'|<(.*)>|([^\s]+))/g;
|
||||
}
|
||||
}
|
||||
|
||||
#if REGION /* Backward Compatibility */
|
||||
class ShaderTemplates {
|
||||
public static final fragHeader:String = "varying float openfl_Alphav;
|
||||
varying vec4 openfl_ColorMultiplierv;
|
||||
varying vec4 openfl_ColorOffsetv;
|
||||
varying vec2 openfl_TextureCoordv;
|
||||
|
||||
uniform bool openfl_HasColorTransform;
|
||||
uniform vec2 openfl_TextureSize;
|
||||
uniform sampler2D bitmap;
|
||||
|
||||
uniform bool hasTransform;
|
||||
uniform bool hasColorTransform;
|
||||
|
||||
vec4 applyFlixelEffects(vec4 color) {
|
||||
if(!hasTransform) {
|
||||
return color;
|
||||
}
|
||||
|
||||
if(color.a == 0.0) {
|
||||
return vec4(0.0, 0.0, 0.0, 0.0);
|
||||
}
|
||||
|
||||
if(!hasColorTransform) {
|
||||
return color * openfl_Alphav;
|
||||
}
|
||||
|
||||
color.rgb = color.rgb / color.a;
|
||||
color = clamp(openfl_ColorOffsetv + (color * openfl_ColorMultiplierv), 0.0, 1.0);
|
||||
|
||||
if(color.a > 0.0) {
|
||||
return vec4(color.rgb * color.a * openfl_Alphav, color.a * openfl_Alphav);
|
||||
}
|
||||
return vec4(0.0, 0.0, 0.0, 0.0);
|
||||
}
|
||||
|
||||
vec4 flixel_texture2D(sampler2D bitmap, vec2 coord) {
|
||||
vec4 color = texture2D(bitmap, coord);
|
||||
return applyFlixelEffects(color);
|
||||
}
|
||||
|
||||
uniform vec4 _camSize;
|
||||
|
||||
float map(float value, float min1, float max1, float min2, float max2) {
|
||||
return min2 + (value - min1) * (max2 - min2) / (max1 - min1);
|
||||
}
|
||||
|
||||
vec2 getCamPos(vec2 pos) {
|
||||
vec4 size = _camSize / vec4(openfl_TextureSize, openfl_TextureSize);
|
||||
return vec2(map(pos.x, size.x, size.x + size.z, 0.0, 1.0), map(pos.y, size.y, size.y + size.w, 0.0, 1.0));
|
||||
}
|
||||
vec2 camToOg(vec2 pos) {
|
||||
vec4 size = _camSize / vec4(openfl_TextureSize, openfl_TextureSize);
|
||||
return vec2(map(pos.x, 0.0, 1.0, size.x, size.x + size.z), map(pos.y, 0.0, 1.0, size.y, size.y + size.w));
|
||||
}
|
||||
vec4 textureCam(sampler2D bitmap, vec2 pos) {
|
||||
return flixel_texture2D(bitmap, camToOg(pos));
|
||||
}";
|
||||
|
||||
public static final fragBody:String = "gl_FragColor = flixel_texture2D(bitmap, openfl_TextureCoordv);";
|
||||
public static final vertHeader:String = "attribute float openfl_Alpha;
|
||||
attribute vec4 openfl_ColorMultiplier;
|
||||
attribute vec4 openfl_ColorOffset;
|
||||
@@ -679,32 +288,97 @@ attribute vec4 colorMultiplier;
|
||||
attribute vec4 colorOffset;
|
||||
uniform bool hasColorTransform;";
|
||||
|
||||
public static final vertBody:String = "openfl_Alphav = openfl_Alpha;
|
||||
openfl_TextureCoordv = openfl_TextureCoord;
|
||||
public static final vertBody:String = "openfl_TextureCoordv = openfl_TextureCoord;
|
||||
|
||||
if(openfl_HasColorTransform) {
|
||||
openfl_ColorMultiplierv = openfl_ColorMultiplier;
|
||||
openfl_ColorOffsetv = openfl_ColorOffset / 255.0;
|
||||
if (hasColorTransform)
|
||||
{
|
||||
openfl_Alphav = openfl_Alpha * colorMultiplier.a;
|
||||
if (openfl_HasColorTransform)
|
||||
{
|
||||
openfl_ColorOffsetv = (openfl_ColorOffset / 255.0 * colorMultiplier) + (colorOffset / 255.0);
|
||||
openfl_ColorMultiplierv = openfl_ColorMultiplier * vec4(colorMultiplier.rgb, 1.0);
|
||||
}
|
||||
else
|
||||
{
|
||||
openfl_ColorOffsetv = colorOffset / 255.0;
|
||||
openfl_ColorMultiplierv = vec4(colorMultiplier.rgb, 1.0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
openfl_Alphav = openfl_Alpha * alpha;
|
||||
if (openfl_HasColorTransform)
|
||||
{
|
||||
openfl_ColorOffsetv = (openfl_ColorOffset + colorOffset) / 255.0;
|
||||
openfl_ColorMultiplierv = openfl_ColorMultiplier;
|
||||
}
|
||||
else
|
||||
{
|
||||
openfl_ColorOffsetv = colorOffset / 255.0;
|
||||
openfl_ColorMultiplierv = vec4(1.0);
|
||||
}
|
||||
}";
|
||||
|
||||
public static final fragHeader:String = "varying float openfl_Alphav;
|
||||
varying vec4 openfl_ColorMultiplierv;
|
||||
varying vec4 openfl_ColorOffsetv;
|
||||
varying vec2 openfl_TextureCoordv;
|
||||
|
||||
uniform bool openfl_HasColorTransform;
|
||||
uniform vec2 openfl_TextureSize;
|
||||
uniform sampler2D bitmap;
|
||||
uniform bool hasTransform;
|
||||
uniform bool hasColorTransform;
|
||||
uniform bool premultiplyAlpha;
|
||||
|
||||
vec4 apply_flixel_transform(vec4 color)
|
||||
{
|
||||
if (!hasTransform) return color;
|
||||
else if (color.a <= 0.0 || openfl_Alphav == 0.0) return vec4(0.0);
|
||||
|
||||
// this is just solely for ASTC compressed textures.
|
||||
// ...also in flixel_texture2D, it also converts to linear alpha anyway.
|
||||
if (!premultiplyAlpha) color.rgb /= color.a;
|
||||
|
||||
color = clamp(openfl_ColorOffsetv + (color * openfl_ColorMultiplierv), 0.0, 1.0);
|
||||
return vec4(color.rgb * color.a * openfl_Alphav, color.a * openfl_Alphav);
|
||||
}
|
||||
#define applyFlixelEffects(color) apply_flixel_transform(color)
|
||||
|
||||
vec4 flixel_texture2D(sampler2D bitmap, vec2 coord)
|
||||
{
|
||||
return apply_flixel_transform(texture2D(bitmap, coord));
|
||||
}
|
||||
|
||||
openfl_Alphav = openfl_Alpha * alpha;
|
||||
uniform vec4 _camSize;
|
||||
|
||||
if(hasColorTransform) {
|
||||
openfl_ColorOffsetv = colorOffset / 255.0;
|
||||
openfl_ColorMultiplierv = colorMultiplier;
|
||||
float map(float value, float min1, float max1, float min2, float max2) {
|
||||
return min2 + (value - min1) * (max2 - min2) / (max1 - min1);
|
||||
}
|
||||
|
||||
gl_Position = openfl_Matrix * openfl_Position;";
|
||||
vec2 getCamPos(vec2 pos) {
|
||||
vec4 size = _camSize / vec4(openfl_TextureSize, openfl_TextureSize);
|
||||
return vec2(map(pos.x, size.x, size.x + size.z, 0.0, 1.0), map(pos.y, size.y, size.y + size.w, 0.0, 1.0));
|
||||
}
|
||||
vec2 camToOg(vec2 pos) {
|
||||
vec4 size = _camSize / vec4(openfl_TextureSize, openfl_TextureSize);
|
||||
return vec2(map(pos.x, 0.0, 1.0, size.x, size.x + size.z), map(pos.y, 0.0, 1.0, size.y, size.y + size.w));
|
||||
}
|
||||
vec4 textureCam(sampler2D bitmap, vec2 pos) {
|
||||
return flixel_texture2D(bitmap, camToOg(pos));
|
||||
}";
|
||||
|
||||
// TODO: make this ignore comments
|
||||
public static final vertBackCompatVarList:Array<EReg> = [
|
||||
~/attribute float alpha/,
|
||||
~/attribute vec4 colorMultiplier/,
|
||||
~/attribute vec4 colorOffset/,
|
||||
~/uniform bool hasColorTransform/
|
||||
];
|
||||
public static final fragBody:String = "gl_FragColor = flixel_texture2D(bitmap, openfl_TextureCoordv);
|
||||
if (gl_FragColor.a == 0.0) discard;";
|
||||
|
||||
public static final vertHeaderBackCompat:String = "attribute float openfl_Alpha;
|
||||
public static final vertBackCompatVarList:Array<EReg> = [
|
||||
~/attribute float alpha/,
|
||||
~/attribute vec4 colorMultiplier/,
|
||||
~/attribute vec4 colorOffset/,
|
||||
~/uniform bool hasColorTransform/
|
||||
];
|
||||
|
||||
public static final vertHeaderBackCompat:String = "attribute float openfl_Alpha;
|
||||
attribute vec4 openfl_ColorMultiplier;
|
||||
attribute vec4 openfl_ColorOffset;
|
||||
attribute vec4 openfl_Position;
|
||||
@@ -719,7 +393,7 @@ uniform mat4 openfl_Matrix;
|
||||
uniform bool openfl_HasColorTransform;
|
||||
uniform vec2 openfl_TextureSize;";
|
||||
|
||||
public static final vertBodyBackCompat:String = "openfl_Alphav = openfl_Alpha;
|
||||
public static final vertBodyBackCompat:String = "openfl_Alphav = openfl_Alpha;
|
||||
openfl_TextureCoordv = openfl_TextureCoord;
|
||||
|
||||
if(openfl_HasColorTransform) {
|
||||
@@ -728,19 +402,8 @@ if(openfl_HasColorTransform) {
|
||||
}
|
||||
|
||||
gl_Position = openfl_Matrix * openfl_Position;";
|
||||
|
||||
public static final defaultVertexSource:String = "#pragma header
|
||||
|
||||
void main(void) {
|
||||
#pragma body
|
||||
}";
|
||||
|
||||
public static final defaultFragmentSource:String = "#pragma header
|
||||
|
||||
void main(void) {
|
||||
#pragma body
|
||||
}";
|
||||
}
|
||||
#end
|
||||
|
||||
class ShaderTypeException extends Exception {
|
||||
var has:Class<Dynamic>;
|
||||
@@ -753,16 +416,4 @@ class ShaderTypeException extends Exception {
|
||||
this.name = name;
|
||||
super('ShaderTypeException - Tried to set the shader uniform "${name}" as a ${Type.getClassName(has)}, but the shader uniform is a ${Std.string(want)}.');
|
||||
}
|
||||
}
|
||||
|
||||
class ShaderErrorPosition {
|
||||
public var column:Int;
|
||||
public var line:Int;
|
||||
public var message:String;
|
||||
|
||||
public function new(line:String, column:String, message:String) {
|
||||
this.line = Std.parseInt(line);
|
||||
this.column = Std.parseInt(column);
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,471 @@
|
||||
package funkin.backend.shaders;
|
||||
|
||||
import haxe.Exception;
|
||||
import haxe.io.Path;
|
||||
import flixel.graphics.FlxGraphic;
|
||||
import flixel.system.FlxAssets.FlxShader;
|
||||
import flixel.util.FlxSignal.FlxTypedSignal;
|
||||
import flixel.util.FlxStringUtil;
|
||||
import hscript.IHScriptCustomBehaviour;
|
||||
import openfl.display.BitmapData;
|
||||
import openfl.display.ShaderInput;
|
||||
import openfl.display.ShaderParameter;
|
||||
import openfl.display.ShaderParameterType;
|
||||
import openfl.display.Shader;
|
||||
import openfl.display3D._internal.GLProgram;
|
||||
import openfl.display3D._internal.GLShader;
|
||||
import openfl.display3D.Program3D;
|
||||
import openfl.utils._internal.Log;
|
||||
|
||||
using StringTools;
|
||||
@:access(openfl.display3D.Context3D)
|
||||
@:access(openfl.display3D.Program3D)
|
||||
@:access(openfl.display.ShaderInput)
|
||||
@:access(openfl.display.ShaderParameter)
|
||||
class FunkinShader extends FlxShader implements IHScriptCustomBehaviour {
|
||||
#if REGION /* Backward Compatibility */
|
||||
private static var __instanceFields = Type.getInstanceFields(FunkinShader);
|
||||
private static var FRAGMENT_SHADER = 0;
|
||||
private static var VERTEX_SHADER = 1;
|
||||
|
||||
public var glslVer(get, set):String;
|
||||
inline function get_glslVer():String return glVersion;
|
||||
inline function set_glslVer(v:String):String return glVersion = v;
|
||||
|
||||
public var glRawFragmentSource(get, set):String;
|
||||
inline function get_glRawFragmentSource():String return __glFragmentSourceRaw;
|
||||
inline function set_glRawFragmentSource(v:String):String return __glFragmentSourceRaw = v;
|
||||
|
||||
public var glRawVertexSource(get, set):String;
|
||||
inline function get_glRawVertexSource():String return __glVertexSourceRaw;
|
||||
inline function set_glRawVertexSource(v:String):String return __glVertexSourceRaw = v;
|
||||
|
||||
// Unused... cne-openfl uses a different system
|
||||
var __cancelNextProcessGLData:Bool = false;
|
||||
public var onProcessGLData:FlxTypedSignal<(String, String)->Void> = new FlxTypedSignal<(String, String)->Void>();
|
||||
#end
|
||||
|
||||
public static function getShaderCode(key:String, isFragment = true):Null<String> {
|
||||
var path = "shaders/" + key;
|
||||
key = Path.withoutExtension(key);
|
||||
|
||||
final ext = Path.extension(path);
|
||||
if (ext == "") path = path + (isFragment ? ".frag" : ".vert");
|
||||
else isFragment = ext != "vert";
|
||||
|
||||
path = Paths.getPath(path);
|
||||
return Assets.exists(path) ? Assets.getText(path) : null;
|
||||
}
|
||||
|
||||
private static function processGLSLText(source:String, glVersion:String, isFragment:Bool, ?pragmas:Map<String, String>):String
|
||||
return Shader.processGLSLText(_processGLSLText(source, glVersion, isFragment, pragmas), glVersion, isFragment);
|
||||
|
||||
private static function _processGLSLText(source:String, glVersion:String, isFragment:Bool, ?pragmas:Map<String, String>):String {
|
||||
if (pragmas != null) {
|
||||
final pragmaKeyword = ~/#pragma\s+(\w+)/g;
|
||||
source = pragmaKeyword.map(source, (_) -> {
|
||||
var name = pragmaKeyword.matched(1), pragma:String;
|
||||
if (pragmas.exists(name)) pragma = pragmas.get(name);
|
||||
else {
|
||||
if (name != "header" && name != "body") return '#pragma $name';
|
||||
pragma = "";
|
||||
}
|
||||
return _processGLSLText(pragma, glVersion, isFragment, pragmas);
|
||||
});
|
||||
}
|
||||
|
||||
inline function tryGetShaderCode(key:String) {
|
||||
final s = getShaderCode(key, isFragment);
|
||||
if (s == null) {
|
||||
Logs.traceColored([
|
||||
Logs.logText('[Shader] ', RED),
|
||||
Logs.logText('Failed to import shader $key', RED),
|
||||
]);
|
||||
return "";
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
final includeKeyword = ~/#include ['"](.+)['"]/g;
|
||||
final importKeyword = ~/#import\s+<(.*)>/g;
|
||||
source = importKeyword.map(source, (_) ->
|
||||
return _processGLSLText(tryGetShaderCode(importKeyword.matched(1)), glVersion, isFragment, pragmas) ?? "");
|
||||
|
||||
return source = includeKeyword.map(source, (_) ->
|
||||
return _processGLSLText(tryGetShaderCode(includeKeyword.matched(1)), glVersion, isFragment, pragmas) ?? "");
|
||||
}
|
||||
|
||||
private static var __defaultsAvailable:Bool;
|
||||
private static var __glFragmentSourceDefault:String;
|
||||
private static var __glVertexSourceDefault:String;
|
||||
private static var __glFragmentPragmasDefault:Map<String, String>;
|
||||
private static var __glVertexPragmasDefault:Map<String, String>;
|
||||
private static var __glFragmentExtensionsDefault:Array<ShaderExtension>;
|
||||
private static var __glVertexExtensionsDefault:Array<ShaderExtension>;
|
||||
|
||||
public var onGLUpdate:FlxTypedSignal<Void->Void> = new FlxTypedSignal<Void->Void>();
|
||||
|
||||
public var fileName:String = "FunkinShader";
|
||||
public var fragFileName:String = "FunkinShader";
|
||||
public var vertFileName:String = "FunkinShader";
|
||||
|
||||
public var shaderPrefix:String = "";
|
||||
public var fragmentPrefix:String = "";
|
||||
public var vertexPrefix:String = "";
|
||||
|
||||
private var __immediate:Bool;
|
||||
|
||||
public function new(?fragmentSource:String, ?vertexSource:String, ?version:String,
|
||||
?fragmentExtensions:Array<ShaderExtension>, ?vertexExtensions:Array<ShaderExtension>, immediate = false
|
||||
) {
|
||||
if (!__defaultsAvailable) {
|
||||
__glFragmentSourceDefault = __glFragmentSourceRaw;
|
||||
__glVertexSourceDefault = __glVertexSourceRaw;
|
||||
__glFragmentPragmasDefault = __glFragmentPragmas.copy();
|
||||
__glVertexPragmasDefault = __glVertexPragmas.copy();
|
||||
__glFragmentExtensionsDefault = __glFragmentExtensions ?? [];
|
||||
__glVertexExtensionsDefault = __glVertexExtensions ?? [];
|
||||
__defaultsAvailable = true;
|
||||
}
|
||||
|
||||
__immediate = immediate;
|
||||
|
||||
if (version != null) glVersion = version;
|
||||
if (vertexExtensions != null) glVertexExtensions = vertexExtensions;
|
||||
if (fragmentExtensions != null) glFragmentExtensions = fragmentExtensions;
|
||||
if (vertexSource != null) glVertexSource = vertexSource;
|
||||
if (fragmentSource != null) glFragmentSource = fragmentSource;
|
||||
|
||||
super();
|
||||
|
||||
if (!__isGenerated) {
|
||||
__isGenerated = true;
|
||||
__init();
|
||||
}
|
||||
}
|
||||
|
||||
public function loadShader(name:String, ?version:String, immediate = false):FunkinShader {
|
||||
final fragment = getShaderCode(name, true), vertex = getShaderCode(name, false);
|
||||
|
||||
glVersion = version;
|
||||
glVertexSource = vertex ?? __glVertexSourceDefault;
|
||||
glFragmentSource = fragment ?? __glFragmentSourceDefault;
|
||||
|
||||
if (immediate) __init();
|
||||
return this;
|
||||
}
|
||||
|
||||
override function __initGL():Void {
|
||||
if (__immediate) {
|
||||
__context = FlxG.stage.context3D;
|
||||
__enable();
|
||||
}
|
||||
super.__initGL();
|
||||
}
|
||||
|
||||
override function __updateGL():Void {
|
||||
onGLUpdate.dispatch();
|
||||
super.__updateGL();
|
||||
}
|
||||
|
||||
public function hget(name:String):Dynamic {
|
||||
if (__glSourceDirty || __data == null) __init();
|
||||
|
||||
if (thisHasField(name) || thisHasField('get_${name}')) return Reflect.getProperty(this, name);
|
||||
else if (!Reflect.hasField(__data, name)) return null;
|
||||
|
||||
final field:Dynamic = Reflect.field(__data, name);
|
||||
|
||||
var cl:String = Type.getClassName(Type.getClass(field));
|
||||
|
||||
// little problem we are facing boys...
|
||||
|
||||
// cant do "field is ShaderInput" because ShaderInput has the @:generic metadata
|
||||
// aka instead of ShaderInput<Float> it gets built as ShaderInput_Float
|
||||
// this should be fine tho because we check the class, and the fields don't vary based on the type
|
||||
|
||||
// thanks for looking in the code cne fans :D!! -lunar
|
||||
|
||||
if (cl.startsWith("openfl.display.ShaderParameter"))
|
||||
return (field.__length > 1) ? field.value : field.value[0];
|
||||
else if (cl.startsWith("openfl.display.ShaderInput"))
|
||||
return field.input;
|
||||
return field;
|
||||
}
|
||||
|
||||
public function hset(name:String, val:Dynamic):Dynamic {
|
||||
if (__glSourceDirty || __data == null) __init();
|
||||
|
||||
if (thisHasField(name) || thisHasField('set_${name}')) {
|
||||
Reflect.setProperty(this, name, val);
|
||||
return val;
|
||||
}
|
||||
else if (!Reflect.hasField(__data, name)) {
|
||||
// ??? huh
|
||||
Reflect.setField(__data, name, val);
|
||||
return val;
|
||||
}
|
||||
|
||||
var field = Reflect.field(__data, name);
|
||||
var cl = Type.getClassName(Type.getClass(field));
|
||||
var isNotNull = val != null;
|
||||
// cant do "field is ShaderInput" for some reason
|
||||
if (cl.startsWith("openfl.display.ShaderParameter")) {
|
||||
if (field.__length <= 1) {
|
||||
// that means we wait for a single number, instead of an array
|
||||
if (field.__isInt && isNotNull && !(val is Int)) {
|
||||
throw new ShaderTypeException(name, Type.getClass(val), 'Int');
|
||||
return null;
|
||||
} else
|
||||
if (field.__isBool && isNotNull && !(val is Bool)) {
|
||||
throw new ShaderTypeException(name, Type.getClass(val), 'Bool');
|
||||
return null;
|
||||
} else
|
||||
if (field.__isFloat && isNotNull && !(val is Float)) {
|
||||
throw new ShaderTypeException(name, Type.getClass(val), 'Float');
|
||||
return null;
|
||||
}
|
||||
return field.value = isNotNull ? [val] : null;
|
||||
} else {
|
||||
if (isNotNull && !(val is Array)) {
|
||||
throw new ShaderTypeException(name, Type.getClass(val), Array);
|
||||
return null;
|
||||
}
|
||||
return field.value = val;
|
||||
}
|
||||
} else if (cl.startsWith("openfl.display.ShaderInput")) {
|
||||
// shader input!!
|
||||
var bitmap:BitmapData;
|
||||
if (!isNotNull) bitmap = null;
|
||||
else if (val is BitmapData) bitmap = val;
|
||||
else if (val is FlxGraphic) bitmap = val.bitmap;
|
||||
else {
|
||||
throw new ShaderTypeException(name, Type.getClass(val), BitmapData);
|
||||
return null;
|
||||
}
|
||||
field.input = bitmap;
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
override function __buildSourcePrefix(isFragment:Bool):String {
|
||||
var result = super.__buildSourcePrefix(isFragment) + '\n$shaderPrefix';
|
||||
return isFragment ? result + '\n$fragmentPrefix' : result + '\n$vertexPrefix';
|
||||
}
|
||||
|
||||
override function set_glFragmentExtensions(value:Array<ShaderExtension>):Array<ShaderExtension> {
|
||||
if (value == null) value = __glFragmentExtensionsDefault;
|
||||
if (value != __glFragmentExtensions) __glSourceDirty = true;
|
||||
return __glFragmentExtensions = value;
|
||||
}
|
||||
|
||||
override function set_glVertexExtensions(value:Array<ShaderExtension>):Array<ShaderExtension> {
|
||||
if (value == null) value = __glVertexExtensionsDefault;
|
||||
if (value != __glVertexExtensions) __glSourceDirty = true;
|
||||
return __glVertexExtensions = value;
|
||||
}
|
||||
|
||||
override function set_glVersion(value:Null<String>):String {
|
||||
if (value == null || value == "") value = Flags.DEFAULT_GLSL_VERSION;
|
||||
if ((__glVersionRaw = value) != __glVersion) {
|
||||
__glSourceDirty = true;
|
||||
if (__glVertexSourceRaw != null) __glVertexSource = processGLSLText(__glVertexSourceRaw, value, false, __glVertexPragmas);
|
||||
if (__glFragmentSourceRaw != null) __glFragmentSource = processGLSLText(__glFragmentSourceRaw, value, true, __glFragmentPragmas);
|
||||
}
|
||||
|
||||
return __glVersion = value;
|
||||
}
|
||||
|
||||
override function set_glFragmentSource(value:String):String {
|
||||
if (value == null || value == "") value = __glFragmentSourceDefault;
|
||||
if ((__glFragmentSourceRaw = value) != null) {
|
||||
if (__glVersion != (__glVersion = Shader.getGLSLTextVersion(value, __glVersionRaw)))
|
||||
__glSourceDirty = true;
|
||||
|
||||
value = processGLSLText(value, __glVersion, true, __glFragmentPragmas);
|
||||
}
|
||||
|
||||
if (value != __glFragmentSource) __glSourceDirty = true;
|
||||
return __glFragmentSource = value;
|
||||
}
|
||||
|
||||
override function set_glVertexSource(value:String):String {
|
||||
if (value == null || value == "") value = __glVertexSourceDefault;
|
||||
if ((__glVertexSourceRaw = value) != null) {
|
||||
if (__glVersion != (__glVersion = Shader.getGLSLTextVersion(value, __glVersionRaw)))
|
||||
__glSourceDirty = true;
|
||||
|
||||
value = processGLSLText(value, __glVersion, false, __glVertexPragmas);
|
||||
}
|
||||
|
||||
if (value != __glVertexSource) __glSourceDirty = true;
|
||||
return __glVertexSource = value;
|
||||
}
|
||||
|
||||
override function set_glFragmentPragmas(value:Map<String, String>):Map<String, String> {
|
||||
if (value == null) value = __glFragmentPragmasDefault;
|
||||
if (value != __glFragmentPragmas)
|
||||
__glSourceDirty = true;
|
||||
|
||||
return __glFragmentPragmas = value;
|
||||
}
|
||||
|
||||
override function set_glVertexPragmas(value:Map<String, String>):Map<String, String> {
|
||||
if (value == null) value = __glVertexPragmasDefault;
|
||||
if (value != __glVertexPragmas)
|
||||
__glSourceDirty = true;
|
||||
|
||||
return __glVertexPragmas = value;
|
||||
}
|
||||
|
||||
function registerParameter(name:String, type:String, isUniform:Bool):Void {
|
||||
__registerParameter(name, Program3D.getParameterTypeFromGLString(type, 1), 1, -1, isUniform, false, null);
|
||||
}
|
||||
|
||||
public function toString():String
|
||||
return FlxStringUtil.getDebugString([for (field in Reflect.fields(data)) LabelValuePair.weak(field, Reflect.field(data, field))]);
|
||||
}
|
||||
|
||||
class ShaderTemplates {
|
||||
#if REGION /* Backward Compatibility */
|
||||
public static final vertHeader:String = "attribute float openfl_Alpha;
|
||||
attribute vec4 openfl_ColorMultiplier;
|
||||
attribute vec4 openfl_ColorOffset;
|
||||
attribute vec4 openfl_Position;
|
||||
attribute vec2 openfl_TextureCoord;
|
||||
|
||||
varying float openfl_Alphav;
|
||||
varying vec4 openfl_ColorMultiplierv;
|
||||
varying vec4 openfl_ColorOffsetv;
|
||||
varying vec2 openfl_TextureCoordv;
|
||||
|
||||
uniform mat4 openfl_Matrix;
|
||||
uniform bool openfl_HasColorTransform;
|
||||
uniform vec2 openfl_TextureSize;
|
||||
|
||||
attribute float alpha;
|
||||
attribute vec4 colorMultiplier;
|
||||
attribute vec4 colorOffset;
|
||||
|
||||
uniform bool hasColorTransform;";
|
||||
|
||||
public static final vertBody:String = "openfl_TextureCoordv = openfl_TextureCoord;
|
||||
|
||||
if (hasColorTransform) {
|
||||
openfl_Alphav = openfl_Alpha * colorMultiplier.a;
|
||||
if (openfl_HasColorTransform) {
|
||||
openfl_ColorOffsetv = (openfl_ColorOffset / 255.0 * colorMultiplier) + (colorOffset / 255.0);
|
||||
openfl_ColorMultiplierv = openfl_ColorMultiplier * vec4(colorMultiplier.rgb, 1.0);
|
||||
}
|
||||
else {
|
||||
openfl_ColorOffsetv = colorOffset / 255.0;
|
||||
openfl_ColorMultiplierv = vec4(colorMultiplier.rgb, 1.0);
|
||||
}
|
||||
}
|
||||
else {
|
||||
openfl_Alphav = openfl_Alpha * alpha;
|
||||
if (openfl_HasColorTransform) {
|
||||
openfl_ColorOffsetv = (openfl_ColorOffset + colorOffset) / 255.0;
|
||||
openfl_ColorMultiplierv = openfl_ColorMultiplier;
|
||||
}
|
||||
else {
|
||||
openfl_ColorOffsetv = colorOffset / 255.0;
|
||||
openfl_ColorMultiplierv = vec4(1.0);
|
||||
}
|
||||
}";
|
||||
|
||||
public static final fragHeader:String = "varying float openfl_Alphav;
|
||||
varying vec4 openfl_ColorMultiplierv;
|
||||
varying vec4 openfl_ColorOffsetv;
|
||||
varying vec2 openfl_TextureCoordv;
|
||||
|
||||
uniform bool openfl_HasColorTransform;
|
||||
uniform vec2 openfl_TextureSize;
|
||||
uniform sampler2D bitmap;
|
||||
|
||||
uniform bool hasTransform;
|
||||
uniform bool hasColorTransform;
|
||||
|
||||
vec4 apply_flixel_transform(vec4 color) {
|
||||
if (!hasTransform) return color;
|
||||
else if (color.a <= 0.0 || openfl_Alphav == 0.0) return vec4(0.0);
|
||||
|
||||
color.rgb /= color.a;
|
||||
color = clamp(openfl_ColorOffsetv + (color * openfl_ColorMultiplierv), 0.0, 1.0);
|
||||
return vec4(color.rgb * color.a * openfl_Alphav, color.a * openfl_Alphav);
|
||||
}
|
||||
|
||||
#define applyFlixelEffects(color) apply_flixel_transform(color)
|
||||
|
||||
vec4 flixel_texture2D(sampler2D bitmap, vec2 coord) {
|
||||
return apply_flixel_transform(texture2D(bitmap, coord));
|
||||
}
|
||||
|
||||
uniform vec4 _camSize;
|
||||
|
||||
float map(float value, float min1, float max1, float min2, float max2) {
|
||||
return min2 + (value - min1) * (max2 - min2) / (max1 - min1);
|
||||
}
|
||||
|
||||
vec2 getCamPos(vec2 pos) {
|
||||
vec4 size = _camSize / vec4(openfl_TextureSize, openfl_TextureSize);
|
||||
return vec2(map(pos.x, size.x, size.x + size.z, 0.0, 1.0), map(pos.y, size.y, size.y + size.w, 0.0, 1.0));
|
||||
}
|
||||
vec2 camToOg(vec2 pos) {
|
||||
vec4 size = _camSize / vec4(openfl_TextureSize, openfl_TextureSize);
|
||||
return vec2(map(pos.x, 0.0, 1.0, size.x, size.x + size.z), map(pos.y, 0.0, 1.0, size.y, size.y + size.w));
|
||||
}
|
||||
vec4 textureCam(sampler2D bitmap, vec2 pos) {
|
||||
return flixel_texture2D(bitmap, camToOg(pos));
|
||||
}";
|
||||
|
||||
public static final fragBody:String = "gl_FragColor = flixel_texture2D(bitmap, openfl_TextureCoordv);
|
||||
if (gl_FragColor.a == 0.0) discard;";
|
||||
|
||||
public static final vertBackCompatVarList:Array<EReg> = [
|
||||
~/attribute float alpha/,
|
||||
~/attribute vec4 colorMultiplier/,
|
||||
~/attribute vec4 colorOffset/,
|
||||
~/uniform bool hasColorTransform/
|
||||
];
|
||||
|
||||
public static final vertHeaderBackCompat:String = "attribute float openfl_Alpha;
|
||||
attribute vec4 openfl_ColorMultiplier;
|
||||
attribute vec4 openfl_ColorOffset;
|
||||
attribute vec4 openfl_Position;
|
||||
attribute vec2 openfl_TextureCoord;
|
||||
|
||||
varying float openfl_Alphav;
|
||||
varying vec4 openfl_ColorMultiplierv;
|
||||
varying vec4 openfl_ColorOffsetv;
|
||||
varying vec2 openfl_TextureCoordv;
|
||||
|
||||
uniform mat4 openfl_Matrix;
|
||||
uniform bool openfl_HasColorTransform;
|
||||
uniform vec2 openfl_TextureSize;";
|
||||
|
||||
public static final vertBodyBackCompat:String = "openfl_Alphav = openfl_Alpha;
|
||||
openfl_TextureCoordv = openfl_TextureCoord;
|
||||
|
||||
if(openfl_HasColorTransform) {
|
||||
openfl_ColorMultiplierv = openfl_ColorMultiplier;
|
||||
openfl_ColorOffsetv = openfl_ColorOffset / 255.0;
|
||||
}
|
||||
|
||||
gl_Position = openfl_Matrix * openfl_Position;";
|
||||
#end
|
||||
}
|
||||
|
||||
class ShaderTypeException extends Exception {
|
||||
var has:Class<Dynamic>;
|
||||
var want:Class<Dynamic>;
|
||||
var name:String;
|
||||
|
||||
public function new(name:String, has:Class<Dynamic>, want:Dynamic) {
|
||||
this.has = has;
|
||||
this.want = want;
|
||||
this.name = name;
|
||||
super('ShaderTypeException - Tried to set the shader uniform "${name}" as a ${Type.getClassName(has)}, but the shader uniform is a ${Std.string(want)}.');
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,11 @@ import openfl.display.BlendMode;
|
||||
import openfl.geom.ColorTransform;
|
||||
import openfl.geom.Point;
|
||||
import openfl.geom.Rectangle;
|
||||
import openfl.display.TriangleCulling;
|
||||
import openfl.display3D.Context3DWrapMode;
|
||||
import openfl.display3D.Context3DCompareMode;
|
||||
import openfl.filters.BitmapFilter;
|
||||
import openfl.filters.ShaderFilter;
|
||||
|
||||
class FakeCamera extends FlxCamera {
|
||||
public static final instance = new FakeCamera();
|
||||
@@ -21,14 +26,14 @@ class FakeCamera extends FlxCamera {
|
||||
visible = false;
|
||||
}
|
||||
|
||||
override function startTrianglesBatch(graphic:FlxGraphic, smoothing:Bool = false, isColored:Bool = false, ?blend:BlendMode, ?hasColorOffsets:Bool, ?shader:FlxShader) { return null;}
|
||||
override function startQuadBatch(graphic:FlxGraphic, colored:Bool, hasColorOffsets:Bool = false, ?blend:BlendMode, smooth:Bool = false, ?shader:FlxShader) { return null;}
|
||||
override function startTrianglesBatch(graphic:FlxGraphic, smoothing:Bool = false, isColored:Bool = false, ?blend:BlendMode, ?hasColorOffsets:Bool, ?shader:FlxShader, ?wrapMode:Context3DWrapMode, ?depthCompareMode:Context3DCompareMode, ?culling:TriangleCulling) { return null;}
|
||||
override function startQuadBatch(graphic:FlxGraphic, colored:Bool, hasColorOffsets:Bool = false, ?blend:BlendMode, smooth:Bool = false, ?shader:FlxShader, ?wrapMode:Context3DWrapMode, ?depthCompareMode:Context3DCompareMode) { return null;}
|
||||
override function clearDrawStack() {}
|
||||
override function render() {}
|
||||
|
||||
public override function drawPixels(?frame:FlxFrame, ?pixels:BitmapData, matrix:FlxMatrix, ?transform:ColorTransform, ?blend:BlendMode, ?smoothing:Bool = false, ?shader:FlxShader) {}
|
||||
public override function copyPixels(?frame:FlxFrame, ?pixels:BitmapData, ?sourceRect:Rectangle, destPoint:Point, ?transform:ColorTransform, ?blend:BlendMode, ?smoothing:Bool = false, ?shader:FlxShader) {}
|
||||
public override function drawTriangles(graphic:FlxGraphic, vertices:DrawData<Float>, indices:DrawData<Int>, uvtData:DrawData<Float>, ?colors:DrawData<Int>, ?position:FlxPoint, ?blend:BlendMode, repeat:Bool = false, smoothing:Bool = false, ?transform:ColorTransform, ?shader:FlxShader):Void {}
|
||||
public override function drawPixels(?frame:FlxFrame, ?pixels:BitmapData, matrix:FlxMatrix, ?transform:ColorTransform, ?blend:BlendMode, ?smoothing:Bool = false, ?shader:FlxShader, ?wrapMode:Context3DWrapMode, ?depthCompareMode:Context3DCompareMode) {}
|
||||
public override function copyPixels(?frame:FlxFrame, ?pixels:BitmapData, ?sourceRect:Rectangle, destPoint:Point, ?transform:ColorTransform, ?blend:BlendMode, ?smoothing:Bool = false, ?shader:FlxShader, ?wrapMode:Context3DWrapMode, ?depthCompareMode:Context3DCompareMode) {}
|
||||
public override function drawTriangles(graphic:FlxGraphic, vertices:DrawData<Float>, indices:DrawData<Int>, uvtData:DrawData<Float>, ?colors:DrawData<Int>, ?position:FlxPoint, ?blend:BlendMode, repeat:Bool = false, smoothing:Bool = false, ?transform:ColorTransform, ?shader:FlxShader, ?wrapMode:Context3DWrapMode, ?depthCompareMode:Context3DCompareMode, ?culling:TriangleCulling):Void {}
|
||||
|
||||
public override function update(elapsed:Float) {}
|
||||
|
||||
@@ -41,10 +46,10 @@ class FakeCallCamera extends FakeCamera {
|
||||
public static final instance = new FakeCallCamera();
|
||||
public var ignoreDraws:Bool = false;
|
||||
|
||||
public dynamic function onDraw(?frame:FlxFrame, ?pixels:BitmapData, matrix:FlxMatrix, ?transform:ColorTransform, ?blend:BlendMode, ?smoothing:Bool = false, ?shader:FlxShader) {
|
||||
public dynamic function onDraw(?frame:FlxFrame, ?pixels:BitmapData, matrix:FlxMatrix, ?transform:ColorTransform, ?blend:BlendMode, ?smoothing:Bool = false, ?shader:FlxShader, ?wrapMode:Context3DWrapMode, ?depthCompareMode:Context3DCompareMode) {
|
||||
}
|
||||
|
||||
override function drawPixels(?frame:FlxFrame, ?pixels:BitmapData, matrix:FlxMatrix, ?transform:ColorTransform, ?blend:BlendMode, ?smoothing:Bool = false, ?shader:FlxShader) {
|
||||
if (!ignoreDraws) onDraw(frame, pixels, matrix, transform, blend, smoothing, shader);
|
||||
override function drawPixels(?frame:FlxFrame, ?pixels:BitmapData, matrix:FlxMatrix, ?transform:ColorTransform, ?blend:BlendMode, ?smoothing:Bool = false, ?shader:FlxShader, ?wrapMode:Context3DWrapMode, ?depthCompareMode:Context3DCompareMode) {
|
||||
if (!ignoreDraws) onDraw(frame, pixels, matrix, transform, blend, smoothing, shader, wrapMode, depthCompareMode);
|
||||
}
|
||||
}
|
||||
@@ -107,6 +107,10 @@ class Flags {
|
||||
public static var DEFAULT_STEPS_PER_BEAT:Int = 4;
|
||||
public static var DEFAULT_LOOP_TIME:Float = 0.0;
|
||||
public static var ICONS_AUTOPOSITION:Bool = true;
|
||||
|
||||
@:lazy public static var DEFAULT_SOUND_TIME_SCALED_PITCH:Null<Bool> = null;
|
||||
@:lazy public static var USE_FLXTRAIL_FRAMES:Null<Bool> = null;
|
||||
|
||||
public static var SUPPORTED_CHART_RUNTIME_FORMATS:Array<String> = ["Legacy", "Psych Engine"];
|
||||
public static var SUPPORTED_CHART_FORMATS:Array<String> = ["BaseGame"];
|
||||
|
||||
@@ -282,7 +286,7 @@ class Flags {
|
||||
public static var DEFAULT_CHARACTER_GHOSTDISABLE_SOUND:String = "editors/character/ghostDisable";
|
||||
public static var DEFAULT_CHARACTER_GHOSTENABLE_SOUND:String = "editors/character/ghostEnable";
|
||||
|
||||
public static var DEFAULT_GLSL_VERSION:String = "120";
|
||||
@:lazy public static var DEFAULT_GLSL_VERSION:String = null;
|
||||
@:also(funkin.backend.utils.HttpUtil.userAgent)
|
||||
public static var USER_AGENT:String = 'request';
|
||||
// -- End of Codename's Default Flags --
|
||||
@@ -317,6 +321,20 @@ class Flags {
|
||||
if (USE_LEGACY_TIMING == null) USE_LEGACY_TIMING = MOD_API_VERSION < 2;
|
||||
if (USE_LEGACY_ZOOM_FACTOR == null) USE_LEGACY_ZOOM_FACTOR = MOD_API_VERSION < 2;
|
||||
if (SUSTAINS_AS_ONE_NOTE == null) SUSTAINS_AS_ONE_NOTE = MOD_API_VERSION >= 2;
|
||||
if (DEFAULT_GLSL_VERSION == null) {
|
||||
if (MOD_API_VERSION < 2) {
|
||||
DEFAULT_GLSL_VERSION = #if (android || mac || web) "100" #else "120" #end;
|
||||
Logs.warn("Blend Mode Extensions won't work in MOD_API_VERSION below than 2");
|
||||
}
|
||||
else {
|
||||
DEFAULT_GLSL_VERSION = openfl.utils.GLSLSourceAssembler.getDefaultVersion();
|
||||
}
|
||||
}
|
||||
if (DEFAULT_SOUND_TIME_SCALED_PITCH == null) DEFAULT_SOUND_TIME_SCALED_PITCH = MOD_API_VERSION >= 2;
|
||||
if (USE_FLXTRAIL_FRAMES == null) USE_FLXTRAIL_FRAMES = MOD_API_VERSION < 2;
|
||||
|
||||
flixel.sound.FlxSound.defaultTimeScaledPitch = cast DEFAULT_SOUND_TIME_SCALED_PITCH;
|
||||
flixel.addons.effects.FlxTrail.defaultDelayBackwardCompatibility = cast USE_FLXTRAIL_FRAMES;
|
||||
}
|
||||
|
||||
public static function loadFromDatas(datas:Array<String>):Map<String, String> {
|
||||
|
||||
@@ -8,13 +8,12 @@ class FunkinGame extends FlxGame {
|
||||
super.switchState();
|
||||
// draw once to put all images in gpu then put the last update time to now to prevent lag spikes or whatever
|
||||
draw();
|
||||
_total = ticks = getTicks();
|
||||
ticks = getTicks();
|
||||
skipNextTickUpdate = true;
|
||||
}
|
||||
|
||||
public override function onEnterFrame(t) {
|
||||
if (skipNextTickUpdate != (skipNextTickUpdate = false))
|
||||
_total = ticks = getTicks();
|
||||
super.onEnterFrame(t);
|
||||
override function __enterFrame(deltaTime:Float) {
|
||||
if (skipNextTickUpdate != (skipNextTickUpdate = false)) ticks = getTicks();
|
||||
super.__enterFrame(deltaTime);
|
||||
}
|
||||
}
|
||||
@@ -36,7 +36,7 @@ class GraphicCacheSprite extends FlxSprite {
|
||||
if (graphic == null) return;
|
||||
|
||||
// make their useCount one time higher to prevent them from auto being cleared from cache
|
||||
graphic.useCount++;
|
||||
graphic.incrementUseCount();
|
||||
graphic.destroyOnNoUse = false;
|
||||
cachedGraphics.push(graphic);
|
||||
nonRenderedCachedGraphics.push(graphic);
|
||||
@@ -45,7 +45,7 @@ class GraphicCacheSprite extends FlxSprite {
|
||||
public override function destroy() {
|
||||
for(g in cachedGraphics) {
|
||||
g.destroyOnNoUse = true;
|
||||
g.useCount--;
|
||||
g.decrementUseCount();
|
||||
}
|
||||
graphic = null;
|
||||
super.destroy();
|
||||
|
||||
@@ -115,7 +115,7 @@ final class Logs {
|
||||
Sys.print(t.text);
|
||||
}
|
||||
NativeAPI.setConsoleColors();
|
||||
Sys.print("\r\n");
|
||||
Sys.println("\r");
|
||||
__showing = false;
|
||||
#elseif mobile
|
||||
while(__showing) {
|
||||
|
||||
@@ -24,8 +24,8 @@ import openfl.utils.AssetLibrary;
|
||||
import sys.FileSystem;
|
||||
import sys.io.File;
|
||||
#if android
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import extension.androidtools.content.Context;
|
||||
import extension.androidtools.os.Build;
|
||||
#end
|
||||
|
||||
class Main extends Sprite
|
||||
@@ -33,7 +33,7 @@ class Main extends Sprite
|
||||
public static var instance:Main;
|
||||
|
||||
public static var modToLoad:String = null;
|
||||
public static var forceGPUOnlyBitmapsOff:Bool = #if desktop false #else true #end;
|
||||
public static var forceGPUOnlyBitmapsOff:Bool = false;
|
||||
public static var noTerminalColor:Bool = false;
|
||||
public static var verbose:Bool = false;
|
||||
|
||||
@@ -58,9 +58,11 @@ class Main extends Sprite
|
||||
// You can pretty much ignore everything from here on - your code should go in your states.
|
||||
|
||||
public static function preInit() {
|
||||
#if sys
|
||||
funkin.backend.utils.NativeAPI.registerAsDPICompatible();
|
||||
funkin.backend.system.CommandLineHandler.parseCommandLine(Sys.args());
|
||||
funkin.backend.system.Main.fixWorkingDirectory();
|
||||
#end
|
||||
}
|
||||
|
||||
public function new()
|
||||
@@ -95,16 +97,10 @@ class Main extends Sprite
|
||||
// DEPRECATED
|
||||
@:dox(hide) public static function execAsync(func:Void->Void) ThreadUtil.execAsync(func);
|
||||
|
||||
private static function getTimer():Int {
|
||||
return time = Lib.getTimer();
|
||||
}
|
||||
|
||||
public static function loadGameSettings() {
|
||||
WindowUtils.init();
|
||||
SaveWarning.init();
|
||||
MemoryUtil.init();
|
||||
@:privateAccess
|
||||
FlxG.game.getTimer = getTimer;
|
||||
FunkinCache.init();
|
||||
Paths.assetsTree = new AssetsLibraryList();
|
||||
|
||||
@@ -129,12 +125,11 @@ class Main extends Sprite
|
||||
funkin.options.PlayerSettings.init();
|
||||
Options.load();
|
||||
|
||||
game.focusLostFramerate = 30;
|
||||
FlxG.fixedTimestep = false;
|
||||
|
||||
FlxG.scaleMode = scaleMode = new FunkinRatioScaleMode();
|
||||
|
||||
Conductor.init();
|
||||
AudioSwitchFix.init();
|
||||
EventManager.init();
|
||||
FlxG.signals.focusGained.add(onFocus);
|
||||
FlxG.signals.preStateSwitch.add(onStateSwitch);
|
||||
@@ -206,16 +201,6 @@ class Main extends Sprite
|
||||
// manual asset clearing since base openfl one does'nt clear lime one
|
||||
// does'nt clear bitmaps since flixel fork does it auto
|
||||
|
||||
@:privateAccess {
|
||||
// clear uint8 pools
|
||||
for(length=>pool in openfl.display3D.utils.UInt8Buff._pools) {
|
||||
for(b in pool.clear())
|
||||
b.destroy();
|
||||
}
|
||||
|
||||
openfl.display3D.utils.UInt8Buff._pools.clear();
|
||||
}
|
||||
|
||||
MemoryUtil.clearMajor();
|
||||
}
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ class MainState extends FlxState {
|
||||
ControlsUtil.resetCustomControls();
|
||||
FlxG.bitmap.reset();
|
||||
FlxG.sound.destroy(true);
|
||||
FlxG.sound.resetCache();
|
||||
|
||||
Paths.assetsTree.reset();
|
||||
|
||||
|
||||
@@ -1,62 +1,14 @@
|
||||
package funkin.backend.system;
|
||||
|
||||
import lime.graphics.Image;
|
||||
import lime.graphics.cairo.CairoImageSurface;
|
||||
import openfl.display.BitmapData;
|
||||
import openfl.geom.Rectangle;
|
||||
|
||||
@:deprecated("Use openfl.display.BitmapData.toHardware instead.")
|
||||
class OptimizedBitmapData extends BitmapData {
|
||||
@SuppressWarnings("checkstyle:Dynamic")
|
||||
@:noCompletion private override function __fromImage(image:#if lime Image #else Dynamic #end):Void
|
||||
{
|
||||
#if lime
|
||||
if (image != null && image.buffer != null)
|
||||
{
|
||||
this.image = image;
|
||||
|
||||
width = image.width;
|
||||
height = image.height;
|
||||
rect = new Rectangle(0, 0, image.width, image.height);
|
||||
|
||||
__textureWidth = width;
|
||||
__textureHeight = height;
|
||||
|
||||
#if sys
|
||||
image.format = BGRA32;
|
||||
image.premultiplied = true;
|
||||
#end
|
||||
|
||||
__isValid = true;
|
||||
readable = true;
|
||||
|
||||
if(FlxG.stage.context3D != null) {
|
||||
lock();
|
||||
getTexture(FlxG.stage.context3D);
|
||||
getSurface();
|
||||
|
||||
readable = true;
|
||||
this.image = null;
|
||||
|
||||
// @:privateAccess
|
||||
// if (FlxG.bitmap.__doNotDelete)
|
||||
// MemoryUtil.clearMinor();
|
||||
}
|
||||
}
|
||||
#end
|
||||
}
|
||||
|
||||
@SuppressWarnings("checkstyle:Dynamic")
|
||||
@:dox(hide) public override function getSurface():#if lime CairoImageSurface #else Dynamic #end
|
||||
{
|
||||
#if lime
|
||||
if (__surface == null)
|
||||
{
|
||||
__surface = CairoImageSurface.fromImage(image);
|
||||
}
|
||||
|
||||
return __surface;
|
||||
#else
|
||||
return null;
|
||||
#end
|
||||
super.__fromImage(image);
|
||||
toHardware();
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,12 @@ class RotatingSpriteGroup extends FlxSpriteGroup {
|
||||
if (maxSize <= 0)
|
||||
return super.recycle(ObjectClass, ObjectFactory, Force, Revive);
|
||||
if (group.members.length < maxSize)
|
||||
return group.recycleCreateObject(ObjectClass, ObjectFactory);
|
||||
{
|
||||
if (ObjectFactory != null) return add(ObjectFactory());
|
||||
if (ObjectClass != null) return add(Type.createInstance(ObjectClass, []));
|
||||
|
||||
return null;
|
||||
}
|
||||
var spr = group.members.shift();
|
||||
group.members.push(spr);
|
||||
if (Revive)
|
||||
|
||||
@@ -14,7 +14,7 @@ class AssetTreeInfo extends FramerateCategory {
|
||||
super("Asset Libraries Tree Info");
|
||||
}
|
||||
|
||||
public override function __enterFrame(t:Int) {
|
||||
public override function __enterFrame(t:Float) {
|
||||
if (alpha <= 0.05) return;
|
||||
|
||||
if ((lastUpdateTime += FlxG.rawElapsed) < 1)
|
||||
|
||||
@@ -6,14 +6,17 @@ import openfl.text.TextField;
|
||||
class CodenameBuildField extends TextField {
|
||||
public function new() {
|
||||
super();
|
||||
defaultTextFormat = Framerate.textFormat;
|
||||
autoSize = LEFT;
|
||||
multiline = wordWrap = false;
|
||||
reload();
|
||||
}
|
||||
|
||||
public function reload() {
|
||||
#if COMPILE_EXPERIMENTAL
|
||||
defaultTextFormat = Framerate.textFormat;
|
||||
|
||||
#if TEST_BUILD
|
||||
text = '${Flags.VERSION_MESSAGE} (Test Build)';
|
||||
#elseif COMPILE_EXPERIMENTAL
|
||||
text = '${Flags.VERSION_MESSAGE} (Experimental Build)';
|
||||
#else
|
||||
text = '${Flags.VERSION_MESSAGE}';
|
||||
|
||||
@@ -5,7 +5,7 @@ class ConductorInfo extends FramerateCategory {
|
||||
super("Conductor Info");
|
||||
}
|
||||
|
||||
public override function __enterFrame(t:Int) {
|
||||
public override function __enterFrame(t:Float) {
|
||||
if (alpha <= 0.05) return;
|
||||
_text = 'Current Song Position: ${Math.floor(Conductor.songPosition * 1000) / 1000}';
|
||||
_text += '\n - ${Conductor.curBeat} beats';
|
||||
|
||||
@@ -7,7 +7,7 @@ class FlixelInfo extends FramerateCategory {
|
||||
super("Flixel Info");
|
||||
}
|
||||
|
||||
public override function __enterFrame(t:Int) {
|
||||
public override function __enterFrame(t:Float) {
|
||||
if (alpha <= 0.05) return;
|
||||
|
||||
|
||||
|
||||
@@ -76,6 +76,7 @@ class Framerate extends Sprite {
|
||||
}
|
||||
|
||||
public function reload() {
|
||||
textFormat = new TextFormat(fontName, 12, -1);
|
||||
for(c in categories)
|
||||
c.reload();
|
||||
#if SHOW_BUILD_ON_FPS
|
||||
@@ -100,7 +101,7 @@ class Framerate extends Sprite {
|
||||
|
||||
|
||||
var debugAlpha:Float = 0;
|
||||
public override function __enterFrame(t:Int) {
|
||||
public override function __enterFrame(t:Float) {
|
||||
alpha = CoolUtil.fpsLerp(alpha, debugMode > 0 ? 1 : 0, 0.5);
|
||||
debugAlpha = CoolUtil.fpsLerp(debugAlpha, debugMode > 1 ? 1 : 0, 0.5);
|
||||
|
||||
|
||||
@@ -40,9 +40,11 @@ class FramerateCategory extends Sprite {
|
||||
this.text.y = this.title.y + this.title.height + 2;
|
||||
}
|
||||
|
||||
public function reload() {}
|
||||
public function reload() {
|
||||
for(label in [this.title, this.text]) label.defaultTextFormat = new TextFormat(Framerate.fontName, label == this.title ? 18 : 12, -1);
|
||||
}
|
||||
|
||||
public override function __enterFrame(t:Int) {
|
||||
public override function __enterFrame(t:Float) {
|
||||
if (alpha <= 0.05) return;
|
||||
super.__enterFrame(t);
|
||||
|
||||
|
||||
@@ -35,10 +35,11 @@ class FramerateCounter extends Sprite {
|
||||
}
|
||||
|
||||
public function reload() {
|
||||
for (label in [fpsNum, fpsLabel]) label.defaultTextFormat = new TextFormat(Framerate.fontName, label == fpsNum ? 18 : 12, -1);
|
||||
lastUpdateTime = 0;
|
||||
}
|
||||
|
||||
public override function __enterFrame(t:Int) {
|
||||
public override function __enterFrame(t:Float) {
|
||||
if (alpha <= 0.05) return;
|
||||
|
||||
super.__enterFrame(t);
|
||||
|
||||
@@ -23,16 +23,18 @@ class MemoryCounter extends Sprite {
|
||||
label.y = 0;
|
||||
label.text = "MEM";
|
||||
label.multiline = label.wordWrap = false;
|
||||
label.defaultTextFormat = new TextFormat(Framerate.fontName, 12, -1);
|
||||
label.defaultTextFormat = Framerate.textFormat;
|
||||
label.selectable = false;
|
||||
addChild(label);
|
||||
}
|
||||
memoryPeakText.alpha = 0.5;
|
||||
}
|
||||
|
||||
public function reload() {}
|
||||
public function reload() {
|
||||
for(label in [memoryText, memoryPeakText]) label.defaultTextFormat = Framerate.textFormat;
|
||||
}
|
||||
|
||||
public override function __enterFrame(t:Int) {
|
||||
public override function __enterFrame(t:Float) {
|
||||
if (alpha <= 0.05) return;
|
||||
super.__enterFrame(t);
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ class StatsInfo extends FramerateCategory {
|
||||
super("Asset Libraries Tree Info");
|
||||
}
|
||||
|
||||
public override function __enterFrame(t:Int) {
|
||||
public override function __enterFrame(t:Float) {
|
||||
if (alpha <= 0.05) return;
|
||||
_text = "totalDC: " + Context3DStats.totalDrawCalls();
|
||||
_text += "\nstageDC: " + Context3DStats.contextDrawCalls(DrawCallContext.STAGE);
|
||||
|
||||
@@ -169,7 +169,7 @@ class SystemInfo extends FramerateCategory {
|
||||
super("System Info");
|
||||
}
|
||||
|
||||
public override function __enterFrame(t:Int) {
|
||||
public override function __enterFrame(t:Float) {
|
||||
if (alpha <= 0.05) return;
|
||||
|
||||
_text = __formattedSysText;
|
||||
|
||||
@@ -58,6 +58,9 @@ class Macros {
|
||||
|
||||
final macroPath = 'funkin.backend.system.macros.Macros';
|
||||
Compiler.addMetadata('@:build($macroPath.buildLimeAssetLibrary())', 'lime.utils.AssetLibrary');
|
||||
Compiler.addMetadata('@:build($macroPath.buildLimeApplication())', 'lime.app.Application');
|
||||
Compiler.addMetadata('@:build($macroPath.buildLimeWindow())', 'lime.ui.Window');
|
||||
Compiler.addMetadata('@:build($macroPath.buildOpenflAssets())', 'openfl.utils.Assets');
|
||||
|
||||
//Adds Compat for #if hscript blocks when you have hscript improved
|
||||
if (Context.defined("hscript_improved") && !Context.defined("hscript")) {
|
||||
@@ -73,5 +76,58 @@ class Macros {
|
||||
|
||||
return fields;
|
||||
}
|
||||
|
||||
public static function buildLimeApplication():Array<Field> {
|
||||
final fields:Array<Field> = Context.getBuildFields(), pos:Position = Context.currentPos();
|
||||
for (f in fields) switch (f.kind) {
|
||||
case FFun(func): switch (f.name) {
|
||||
case "exec": switch (func.expr.expr) {
|
||||
case EBlock(exprs): exprs.insert(1, macro funkin.backend.system.Main.preInit());
|
||||
default:
|
||||
}
|
||||
}
|
||||
default:
|
||||
}
|
||||
|
||||
return fields;
|
||||
}
|
||||
|
||||
public static function buildLimeWindow():Array<Field> {
|
||||
final fields:Array<Field> = Context.getBuildFields(), pos:Position = Context.currentPos();
|
||||
if (!Context.defined("DARK_MODE_WINDOW")) return fields;
|
||||
|
||||
for (f in fields) switch (f.kind) {
|
||||
case FFun(func): switch (f.name) {
|
||||
case "new": switch (func.expr.expr) {
|
||||
case EBlock(exprs): exprs.push(macro funkin.backend.utils.NativeAPI.setDarkMode(title, true));
|
||||
default:
|
||||
}
|
||||
}
|
||||
default:
|
||||
}
|
||||
|
||||
return fields;
|
||||
}
|
||||
|
||||
public static function buildOpenflAssets():Array<Field> {
|
||||
final fields:Array<Field> = Context.getBuildFields(), pos:Position = Context.currentPos();
|
||||
for (f in fields) switch (f.name) {
|
||||
case "allowHardwareTextures": fields.remove(f);
|
||||
default:
|
||||
}
|
||||
|
||||
fields.push({name: 'allowHardwareTextures', access: [APublic, AStatic], pos: pos, kind: FProp("get", "set", macro :Bool)});
|
||||
fields.push({name: '__allowHardwareTextures', access: [APrivate, AStatic], pos: pos, kind: FVar(macro :Null<Bool>)});
|
||||
|
||||
fields.push({name: "get_allowHardwareTextures", access: [APublic, AStatic, AInline], pos: pos, kind: FFun({ret: macro :Bool, args: [], expr: macro {
|
||||
return __allowHardwareTextures != null ? __allowHardwareTextures : !funkin.backend.system.Main.forceGPUOnlyBitmapsOff && funkin.options.Options.gpuOnlyBitmaps;
|
||||
}})});
|
||||
fields.push({name: "set_allowHardwareTextures", access: [APublic, AStatic, AInline], pos: pos, kind: FFun({ret: macro :Bool, args: [{name: "value", type: macro :Bool}], expr: macro {
|
||||
__allowHardwareTextures = value;
|
||||
return get_allowHardwareTextures();
|
||||
}})});
|
||||
|
||||
return fields;
|
||||
}
|
||||
}
|
||||
#end
|
||||
@@ -1,31 +0,0 @@
|
||||
package funkin.backend.system.modules;
|
||||
|
||||
import haxe.io.Path;
|
||||
|
||||
/*
|
||||
* A class that simply points OpenALSoft to a custom configuration file when
|
||||
* the game starts up.
|
||||
*
|
||||
* The config overrides a few global OpenALSoft settings with the aim of
|
||||
* improving audio quality on desktop targets.
|
||||
*/
|
||||
@:keepInit class ALSoftConfig
|
||||
{
|
||||
#if desktop
|
||||
static function __init__():Void
|
||||
{
|
||||
var origin:String = #if hl Sys.getCwd() #else Sys.programPath() #end;
|
||||
|
||||
var configPath:String = Path.directory(Path.withoutExtension(origin));
|
||||
#if windows
|
||||
configPath += "/plugins/alsoft.ini";
|
||||
#elseif mac
|
||||
configPath = Path.directory(configPath) + "/Resources/plugins/alsoft.conf";
|
||||
#else
|
||||
configPath += "/plugins/alsoft.conf";
|
||||
#end
|
||||
|
||||
Sys.putEnv("ALSOFT_CONF", configPath);
|
||||
}
|
||||
#end
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
package funkin.backend.system.modules;
|
||||
|
||||
import flixel.FlxState;
|
||||
import flixel.sound.FlxSound;
|
||||
import funkin.backend.utils.NativeAPI;
|
||||
import lime.media.AudioManager;
|
||||
import lime.media.AudioSource;
|
||||
import haxe.Timer;
|
||||
|
||||
/**
|
||||
* if you are stealing this keep this comment at least please lol
|
||||
*
|
||||
* hi gray itsa me yoshicrafter29 i fixed it hehe
|
||||
*/
|
||||
@:dox(hide)
|
||||
class AudioSwitchFix {
|
||||
public static function onAudioDisconnected() @:privateAccess {
|
||||
var sources:Array<{source:AudioSource, playing:Bool, time:Float, gain:Float, pitch:Float, position:lime.math.Vector4}> = [];
|
||||
for (source in AudioSource.activeSources) {
|
||||
var wasPlaying = source.playing;
|
||||
sources.push({
|
||||
source: source,
|
||||
playing: wasPlaying,
|
||||
time: source.currentTime,
|
||||
gain: source.gain,
|
||||
pitch: source.pitch,
|
||||
position: source.position
|
||||
});
|
||||
|
||||
source.__backend.dispose();
|
||||
if (wasPlaying) source.__backend.playing = true;
|
||||
}
|
||||
|
||||
AudioManager.shutdown();
|
||||
AudioManager.init();
|
||||
// #if !lime_doc_gen
|
||||
// if (AudioManager.context.type == OPENAL)
|
||||
// {
|
||||
// var alc = AudioManager.context.openal;
|
||||
|
||||
// var device = alc.openDevice();
|
||||
// var ctx = alc.createContext(device);
|
||||
// alc.makeContextCurrent(ctx);
|
||||
// alc.processContext(ctx);
|
||||
// }
|
||||
// #end
|
||||
|
||||
for (d in sources) {
|
||||
d.source.__backend.init();
|
||||
d.source.currentTime = d.time;
|
||||
d.source.gain = d.gain;
|
||||
d.source.pitch = d.pitch;
|
||||
d.source.position = d.position;
|
||||
|
||||
if (d.playing) d.source.play();
|
||||
}
|
||||
|
||||
Main.changeID++;
|
||||
Main.audioDisconnected = false;
|
||||
}
|
||||
|
||||
private static var timer:Timer;
|
||||
|
||||
private static function onRun() if (Main.audioDisconnected) onAudioDisconnected();
|
||||
public static function init() {
|
||||
NativeAPI.registerAudio();
|
||||
if (timer == null) (timer = new Timer(1000)).run = onRun;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3,220 +3,232 @@ package funkin.backend.utils;
|
||||
import haxe.macro.Expr;
|
||||
|
||||
final class MathUtil {
|
||||
public static inline var EULER:Float = 2.718281828459;
|
||||
public static inline var EULER:Float = 2.718281828459;
|
||||
|
||||
/**
|
||||
* Returns the maximum value in the arguments.
|
||||
* @param args Array of values
|
||||
*
|
||||
* @return The maximum value
|
||||
**/
|
||||
public static function maxInt(...args:Int):Int {
|
||||
var max = args[0];
|
||||
for(i in 1...args.length) {
|
||||
var arg = args[i];
|
||||
if(arg > max)
|
||||
max = arg;
|
||||
}
|
||||
return max;
|
||||
}
|
||||
/**
|
||||
* Returns the maximum value in the arguments.
|
||||
* @param args Array of values
|
||||
*
|
||||
* @return The maximum value
|
||||
**/
|
||||
public static function maxInt(...args:Int):Int {
|
||||
var max = args[0];
|
||||
for(i in 1...args.length) {
|
||||
var arg = args[i];
|
||||
if(arg > max)
|
||||
max = arg;
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the minimum value in the arguments.
|
||||
* @param args Array of values
|
||||
*
|
||||
* @return The minimum value
|
||||
**/
|
||||
public static function minInt(...args:Int):Int {
|
||||
var min = args[0];
|
||||
for(i in 1...args.length) {
|
||||
var arg = args[i];
|
||||
if(arg < min)
|
||||
min = arg;
|
||||
}
|
||||
return min;
|
||||
}
|
||||
/**
|
||||
* Returns the minimum value in the arguments.
|
||||
* @param args Array of values
|
||||
*
|
||||
* @return The minimum value
|
||||
**/
|
||||
public static function minInt(...args:Int):Int {
|
||||
var min = args[0];
|
||||
for(i in 1...args.length) {
|
||||
var arg = args[i];
|
||||
if(arg < min)
|
||||
min = arg;
|
||||
}
|
||||
return min;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maximum value in the arguments.
|
||||
*
|
||||
* NOTE: If you are using this in compile time, you should use `MathUtil.maxSmart` instead of this for better performance.
|
||||
*
|
||||
* @param args Array of values
|
||||
*
|
||||
* @return The maximum value
|
||||
**/
|
||||
public static function max(...args:Float):Float {
|
||||
var max = args[0];
|
||||
for(i in 1...args.length) {
|
||||
var arg = args[i];
|
||||
if(arg > max)
|
||||
max = arg;
|
||||
}
|
||||
return max;
|
||||
}
|
||||
/**
|
||||
* Returns the maximum value in the arguments.
|
||||
*
|
||||
* NOTE: If you are using this in compile time, you should use `MathUtil.maxSmart` instead of this for better performance.
|
||||
*
|
||||
* @param args Array of values
|
||||
*
|
||||
* @return The maximum value
|
||||
**/
|
||||
public static function max(...args:Float):Float {
|
||||
var max = args[0];
|
||||
for(i in 1...args.length) {
|
||||
var arg = args[i];
|
||||
if(arg > max)
|
||||
max = arg;
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the minimum value in the arguments.
|
||||
*
|
||||
* NOTE: If you are using this in compile time, you should use `MathUtil.minSmart` instead of this for better performance.
|
||||
*
|
||||
* @param args Array of values
|
||||
*
|
||||
* @return The minimum value
|
||||
**/
|
||||
public static function min(...args:Float):Float {
|
||||
var min = args[0];
|
||||
for(i in 1...args.length) {
|
||||
var arg = args[i];
|
||||
if(arg < min)
|
||||
min = arg;
|
||||
}
|
||||
return min;
|
||||
}
|
||||
/**
|
||||
* Returns the minimum value in the arguments.
|
||||
*
|
||||
* NOTE: If you are using this in compile time, you should use `MathUtil.minSmart` instead of this for better performance.
|
||||
*
|
||||
* @param args Array of values
|
||||
*
|
||||
* @return The minimum value
|
||||
**/
|
||||
public static function min(...args:Float):Float {
|
||||
var min = args[0];
|
||||
for(i in 1...args.length) {
|
||||
var arg = args[i];
|
||||
if(arg < min)
|
||||
min = arg;
|
||||
}
|
||||
return min;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a is less than b with considering a margin of error.
|
||||
* * @param a Float
|
||||
* @param b Float
|
||||
* @param margin Float (Default: EPSILON)
|
||||
* * @return Bool
|
||||
**/
|
||||
public static function lessThan(a:Float, b:Float, margin:Float = 0.0000001):Bool {
|
||||
return a < b - margin;
|
||||
}
|
||||
/**
|
||||
* Checks if a is less than b with considering a margin of error.
|
||||
*
|
||||
* @param a Float
|
||||
* @param b Float
|
||||
* @param margin Float (Default: EPSILON)
|
||||
*
|
||||
* @return Bool
|
||||
**/
|
||||
public static function lessThan(a:Float, b:Float, margin:Float = 0.0000001):Bool {
|
||||
return a < b - margin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a is less than or equally b with considering a margin of error.
|
||||
* * @param a Float
|
||||
* @param b Float
|
||||
* @param margin Float (Default: EPSILON)
|
||||
* * @return Bool
|
||||
**/
|
||||
public static function lessThanEqual(a:Float, b:Float, margin:Float = 0.0000001):Bool {
|
||||
return a <= b - margin;
|
||||
}
|
||||
/**
|
||||
* Checks if a is less than or equally b with considering a margin of error.
|
||||
*
|
||||
* @param a Float
|
||||
* @param b Float
|
||||
* @param margin Float (Default: EPSILON)
|
||||
*
|
||||
* @return Bool
|
||||
**/
|
||||
public static function lessThanEqual(a:Float, b:Float, margin:Float = 0.0000001):Bool {
|
||||
return a <= b - margin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a is greater than b with considering a margin of error.
|
||||
* * @param a Float
|
||||
* @param b Float
|
||||
* @param margin Float (Default: EPSILON)
|
||||
* * @return Bool
|
||||
**/
|
||||
public static function greaterThan(a:Float, b:Float, margin:Float = 0.0000001):Bool {
|
||||
return a > b + margin;
|
||||
}
|
||||
/**
|
||||
* Checks if a is greater than b with considering a margin of error.
|
||||
*
|
||||
* @param a Float
|
||||
* @param b Float
|
||||
* @param margin Float (Default: EPSILON)
|
||||
*
|
||||
* @return Bool
|
||||
**/
|
||||
public static function greaterThan(a:Float, b:Float, margin:Float = 0.0000001):Bool {
|
||||
return a > b + margin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a is greater than or equally b with considering a margin of error.
|
||||
* * @param a Float
|
||||
* @param b Float
|
||||
* @param margin Float (Default: EPSILON)
|
||||
* * @return Bool
|
||||
**/
|
||||
public static function greaterThanEqual(a:Float, b:Float, margin:Float = 0.0000001):Bool {
|
||||
return a >= b + margin;
|
||||
}
|
||||
/**
|
||||
* Checks if a is greater than or equally b with considering a margin of error.
|
||||
*
|
||||
* @param a Float
|
||||
* @param b Float
|
||||
* @param margin Float (Default: EPSILON)
|
||||
*
|
||||
* @return Bool
|
||||
**/
|
||||
public static function greaterThanEqual(a:Float, b:Float, margin:Float = 0.0000001):Bool {
|
||||
return a >= b + margin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a is approximately equal to b.
|
||||
* * @param a Float
|
||||
* @param b Float
|
||||
* @param margin Float (Default: EPSILON)
|
||||
* * @return Bool
|
||||
**/
|
||||
public static function equal(a:Float, b:Float, margin:Float = 0.0000001):Bool {
|
||||
return Math.abs(a - b) <= margin;
|
||||
}
|
||||
/**
|
||||
* Checks if a is approximately equal to b.
|
||||
*
|
||||
* @param a Float
|
||||
* @param b Float
|
||||
* @param margin Float (Default: EPSILON)
|
||||
*
|
||||
* @return Bool
|
||||
**/
|
||||
public static function equal(a:Float, b:Float, margin:Float = 0.0000001):Bool {
|
||||
return Math.abs(a - b) <= margin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a are not approximately equal to b.
|
||||
* * @param a Float
|
||||
* @param b Float
|
||||
* @param margin Float (Default: EPSILON)
|
||||
* * @return Bool
|
||||
**/
|
||||
public static function notEqual(a:Float, b:Float, margin:Float = 0.0000001):Bool {
|
||||
return Math.abs(a - b) > margin;
|
||||
}
|
||||
/**
|
||||
* Checks if a are not approximately equal to b.
|
||||
*
|
||||
* @param a Float
|
||||
* @param b Float
|
||||
* @param margin Float (Default: EPSILON)
|
||||
*
|
||||
* @return Bool
|
||||
**/
|
||||
public static function notEqual(a:Float, b:Float, margin:Float = 0.0000001):Bool {
|
||||
return Math.abs(a - b) > margin;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param edge0 Float
|
||||
* @param edge1 Float
|
||||
* @param x Float
|
||||
* @return Float
|
||||
**/
|
||||
public static function smoothStep(edge0:Float, edge1:Float, x:Float):Float {
|
||||
var t = (x - edge0) / (edge1 - edge0);
|
||||
var clamped = t < 0.0 ? 0.0 : (t > 1.0 ? 1.0 : t);
|
||||
return clamped * clamped * (3.0 - 2.0 * clamped);
|
||||
}
|
||||
/**
|
||||
* @param edge0 Float
|
||||
* @param edge1 Float
|
||||
* @param x Float
|
||||
* @return Float
|
||||
**/
|
||||
public static function smoothStep(edge0:Float, edge1:Float, x:Float):Float {
|
||||
var t = (x - edge0) / (edge1 - edge0);
|
||||
var clamped = t < 0.0 ? 0.0 : (t > 1.0 ? 1.0 : t);
|
||||
return clamped * clamped * (3.0 - 2.0 * clamped);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param a Float
|
||||
* @param b Float
|
||||
* @param v Float
|
||||
* @return Float
|
||||
**/
|
||||
public static function inverseLerp(a:Float, b:Float, v:Float):Float {
|
||||
return (v - a) / (b - a);
|
||||
}
|
||||
/**
|
||||
* @param a Float
|
||||
* @param b Float
|
||||
* @param v Float
|
||||
* @return Float
|
||||
**/
|
||||
public static function inverseLerp(a:Float, b:Float, v:Float):Float {
|
||||
return (v - a) / (b - a);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param v Float
|
||||
* @return Float
|
||||
**/
|
||||
public static function fract(v:Float):Float {
|
||||
return v - Math.floor(v);
|
||||
}
|
||||
/**
|
||||
* @param v Float
|
||||
* @return Float
|
||||
**/
|
||||
public static function fract(v:Float):Float {
|
||||
return v - Math.floor(v);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut to `Math.max` but with infinite amount of arguments
|
||||
*
|
||||
* Might not preserve the order of arguments, please test this.
|
||||
*
|
||||
* Dont use this in hscript, it doesnt work, it only works on compile time
|
||||
**/
|
||||
@:dox(hide) public static macro function maxSmart(..._args:Expr):Expr {
|
||||
return genericMinMaxSmart(_args.toArray(), "Math.max");
|
||||
}
|
||||
/**
|
||||
* Shortcut to `Math.max` but with infinite amount of arguments
|
||||
*
|
||||
* Might not preserve the order of arguments, please test this.
|
||||
*
|
||||
* Dont use this in hscript, it doesnt work, it only works on compile time
|
||||
**/
|
||||
@:dox(hide) public static macro function maxSmart(..._args:Expr):Expr {
|
||||
return genericMinMaxSmart(_args.toArray(), "Math.max");
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut to `Math.min` but with infinite amount of arguments
|
||||
*
|
||||
* Might not preserve the order of arguments, please test this.
|
||||
*
|
||||
* Dont use this in hscript, it doesnt work, it only works on compile time
|
||||
**/
|
||||
@:dox(hide) public static macro function minSmart(..._args:Expr):Expr {
|
||||
return genericMinMaxSmart(_args.toArray(), "Math.min");
|
||||
}
|
||||
/**
|
||||
* Shortcut to `Math.min` but with infinite amount of arguments
|
||||
*
|
||||
* Might not preserve the order of arguments, please test this.
|
||||
*
|
||||
* Dont use this in hscript, it doesnt work, it only works on compile time
|
||||
**/
|
||||
@:dox(hide) public static macro function minSmart(..._args:Expr):Expr {
|
||||
return genericMinMaxSmart(_args.toArray(), "Math.min");
|
||||
}
|
||||
|
||||
#if macro
|
||||
@:dox(hide) private static function genericMinMaxSmart(_args:Array<Expr>, funcPath:String):Expr {
|
||||
var args = _args.copy();
|
||||
if (args.length == 0) return macro 0;
|
||||
#if macro
|
||||
@:dox(hide) private static function genericMinMaxSmart(_args:Array<Expr>, funcPath:String):Expr {
|
||||
var args = _args.copy();
|
||||
if (args.length == 0) return macro 0;
|
||||
|
||||
var func = funcPath.split(".");
|
||||
var func = funcPath.split(".");
|
||||
|
||||
function nested(lst:Array<Expr>):Expr {
|
||||
if (lst.length == 1) {
|
||||
return macro ${lst[0]};
|
||||
} else if (lst.length == 2) {
|
||||
return macro $p{func}(${lst[0]}, ${lst[1]});
|
||||
} else {
|
||||
var mid = Std.int(lst.length / 2);
|
||||
return macro $p{func}(${nested(lst.slice(0, mid))}, ${nested(lst.slice(mid, lst.length))});
|
||||
}
|
||||
}
|
||||
function nested(lst:Array<Expr>):Expr {
|
||||
if (lst.length == 1) {
|
||||
return macro ${lst[0]};
|
||||
} else if (lst.length == 2) {
|
||||
return macro $p{func}(${lst[0]}, ${lst[1]});
|
||||
} else {
|
||||
var mid = Std.int(lst.length / 2);
|
||||
return macro $p{func}(${nested(lst.slice(0, mid))}, ${nested(lst.slice(mid, lst.length))});
|
||||
}
|
||||
}
|
||||
|
||||
var expr = nested(args);
|
||||
var expr = nested(args);
|
||||
|
||||
//var printer = new haxe.macro.Printer();
|
||||
//trace(printer.printExpr(expr));
|
||||
//var printer = new haxe.macro.Printer();
|
||||
//trace(printer.printExpr(expr));
|
||||
|
||||
return macro $expr;
|
||||
}
|
||||
#end
|
||||
return macro $expr;
|
||||
}
|
||||
#end
|
||||
}
|
||||
|
||||
@@ -13,12 +13,6 @@ import flixel.util.FlxColor;
|
||||
* Some functions might not have effect on some platforms.
|
||||
*/
|
||||
class NativeAPI {
|
||||
@:dox(hide) public static function registerAudio() {
|
||||
#if windows
|
||||
Windows.registerAudio();
|
||||
#end
|
||||
}
|
||||
|
||||
@:dox(hide) public static function registerAsDPICompatible() {
|
||||
#if windows
|
||||
Windows.registerAsDPICompatible();
|
||||
@@ -185,16 +179,7 @@ class NativeAPI {
|
||||
public static function setConsoleColors(foregroundColor:ConsoleColor = NONE, ?backgroundColor:ConsoleColor = NONE) {
|
||||
if(Main.noTerminalColor) return;
|
||||
|
||||
#if (windows && !hl)
|
||||
if(foregroundColor == NONE)
|
||||
foregroundColor = LIGHTGRAY;
|
||||
if(backgroundColor == NONE)
|
||||
backgroundColor = BLACK;
|
||||
|
||||
var fg:Int = cast foregroundColor;
|
||||
var bg:Int = cast backgroundColor;
|
||||
Windows.setConsoleColors((bg * 16) + fg);
|
||||
#elseif sys
|
||||
#if sys
|
||||
Sys.print("\x1b[0m");
|
||||
if(foregroundColor != NONE)
|
||||
Sys.print("\x1b[" + Std.int(consoleColorToANSI(foregroundColor)) + "m");
|
||||
|
||||
@@ -27,107 +27,10 @@ import funkin.backend.utils.NativeAPI.MessageBoxIcon;
|
||||
#include <wingdi.h>
|
||||
#include <shellapi.h>
|
||||
#include <uxtheme.h>
|
||||
|
||||
#define SAFE_RELEASE(punk) \\
|
||||
if ((punk) != NULL) \\
|
||||
{ (punk)->Release(); (punk) = NULL; }
|
||||
|
||||
static long lastDefId = 0;
|
||||
|
||||
class AudioFixClient : public IMMNotificationClient {
|
||||
LONG _cRef;
|
||||
IMMDeviceEnumerator *_pEnumerator;
|
||||
|
||||
public:
|
||||
AudioFixClient() :
|
||||
_cRef(1),
|
||||
_pEnumerator(NULL)
|
||||
{
|
||||
HRESULT result = CoCreateInstance(__uuidof(MMDeviceEnumerator),
|
||||
NULL, CLSCTX_INPROC_SERVER,
|
||||
__uuidof(IMMDeviceEnumerator),
|
||||
(void**)&_pEnumerator);
|
||||
if (result == S_OK) {
|
||||
_pEnumerator->RegisterEndpointNotificationCallback(this);
|
||||
}
|
||||
}
|
||||
|
||||
~AudioFixClient()
|
||||
{
|
||||
SAFE_RELEASE(_pEnumerator);
|
||||
}
|
||||
|
||||
ULONG STDMETHODCALLTYPE AddRef()
|
||||
{
|
||||
return InterlockedIncrement(&_cRef);
|
||||
}
|
||||
|
||||
ULONG STDMETHODCALLTYPE Release()
|
||||
{
|
||||
ULONG ulRef = InterlockedDecrement(&_cRef);
|
||||
if (0 == ulRef)
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
return ulRef;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE QueryInterface(
|
||||
REFIID riid, VOID **ppvInterface)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE OnDeviceAdded(LPCWSTR pwstrDeviceId)
|
||||
{
|
||||
return S_OK;
|
||||
};
|
||||
|
||||
HRESULT STDMETHODCALLTYPE OnDeviceRemoved(LPCWSTR pwstrDeviceId)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE OnDeviceStateChanged(
|
||||
LPCWSTR pwstrDeviceId,
|
||||
DWORD dwNewState)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE OnPropertyValueChanged(
|
||||
LPCWSTR pwstrDeviceId,
|
||||
const PROPERTYKEY key)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged(
|
||||
EDataFlow flow, ERole role,
|
||||
LPCWSTR pwstrDeviceId)
|
||||
{
|
||||
::funkin::backend::_hx_system::Main_obj::audioDisconnected = true;
|
||||
return S_OK;
|
||||
};
|
||||
};
|
||||
|
||||
AudioFixClient *curAudioFix;
|
||||
')
|
||||
@:dox(hide)
|
||||
final class Windows {
|
||||
|
||||
public static var __audioChangeCallback:Void->Void = function() {
|
||||
trace("test");
|
||||
};
|
||||
|
||||
|
||||
@:functionCode('
|
||||
if (!curAudioFix) curAudioFix = new AudioFixClient();
|
||||
')
|
||||
public static function registerAudio() {
|
||||
funkin.backend.system.Main.audioDisconnected = false;
|
||||
}
|
||||
|
||||
@:functionCode('
|
||||
int darkMode = enable ? 1 : 0;
|
||||
|
||||
|
||||
@@ -2,6 +2,9 @@ package funkin.editors;
|
||||
|
||||
import haxe.io.Path;
|
||||
import lime.ui.FileDialog;
|
||||
#if lime_funkin
|
||||
import lime.ui.FileDialogFilter;
|
||||
#end
|
||||
|
||||
class SaveSubstate extends MusicBeatSubstate {
|
||||
public var saveOptions:Map<String, Bool>;
|
||||
@@ -26,6 +29,13 @@ class SaveSubstate extends MusicBeatSubstate {
|
||||
public override function create() {
|
||||
super.create();
|
||||
|
||||
#if lime_funkin
|
||||
FileDialog.saveFile(FlxG.stage.window, "Save File", (fileName:String, activeFilter:FileDialogFilter) -> {
|
||||
CoolUtil.safeSaveFile(fileName, data);
|
||||
close();
|
||||
}, [new FileDialogFilter("Specified File Extension", options.saveExt.getDefault(Path.extension(options.defaultSaveFile)))],
|
||||
options.defaultSaveFile);
|
||||
#else
|
||||
var fileDialog = new FileDialog();
|
||||
fileDialog.onCancel.add(function() close());
|
||||
fileDialog.onSelect.add(function(str) {
|
||||
@@ -33,6 +43,7 @@ class SaveSubstate extends MusicBeatSubstate {
|
||||
close();
|
||||
});
|
||||
fileDialog.browse(SAVE, options.saveExt.getDefault(Path.extension(options.defaultSaveFile)), options.defaultSaveFile);
|
||||
#end
|
||||
}
|
||||
|
||||
public override function update(elapsed:Float) {
|
||||
|
||||
@@ -2,6 +2,9 @@ package funkin.editors.ui;
|
||||
|
||||
import haxe.io.Bytes;
|
||||
import lime.ui.FileDialog;
|
||||
#if lime_funkin
|
||||
import lime.ui.FileDialogFilter;
|
||||
#end
|
||||
|
||||
class UIFileExplorer extends UISliceSprite {
|
||||
public var uploadButton:UIButton;
|
||||
@@ -24,10 +27,16 @@ class UIFileExplorer extends UISliceSprite {
|
||||
|
||||
if (onFile != null) this.onFile = onFile;
|
||||
|
||||
uploadButton = new UIButton(x + 8, y+ 8, null, function () {
|
||||
uploadButton = new UIButton(x + 8, y + 8, null, function () {
|
||||
#if lime_funkin
|
||||
FileDialog.openFile(FlxG.stage.window, "Open File", (fileNames:Array<String>, activeFilter:FileDialogFilter) -> {
|
||||
loadFile(fileNames[0]);
|
||||
}, this.fileType != null ? [new FileDialogFilter("Specified File Extension", this.fileType)] : null);
|
||||
#else
|
||||
var fileDialog = new FileDialog();
|
||||
fileDialog.onSelect.add(loadFile);
|
||||
fileDialog.browse(OPEN, this.fileType);
|
||||
#end
|
||||
}, bWidth - 16, bHeight - 16);
|
||||
members.push(uploadButton);
|
||||
|
||||
|
||||
@@ -72,7 +72,10 @@ class UITextBox extends UISliceSprite implements IUIFocusable {
|
||||
framesOffset = (selected ? 18 : (hovered ? 9 : 0));
|
||||
@:privateAccess {
|
||||
if (selected) {
|
||||
__wasFocused = true;
|
||||
if (!__wasFocused) {
|
||||
__wasFocused = true;
|
||||
FlxG.stage.window.textInputEnabled = true;
|
||||
}
|
||||
caretSpr.alpha = (FlxG.game.ticks % 666) >= 333 ? 1 : 0;
|
||||
|
||||
var curPos = switch (position) {
|
||||
@@ -92,6 +95,7 @@ class UITextBox extends UISliceSprite implements IUIFocusable {
|
||||
} else {
|
||||
if (__wasFocused) {
|
||||
__wasFocused = false;
|
||||
FlxG.stage.window.textInputEnabled = false;
|
||||
if (onChange != null)
|
||||
onChange(label.text);
|
||||
}
|
||||
@@ -133,6 +137,7 @@ class UITextBox extends UISliceSprite implements IUIFocusable {
|
||||
focused = false;
|
||||
if (onChange != null)
|
||||
onChange(label.text);
|
||||
return;
|
||||
case LEFT:
|
||||
if (modifier.ctrlKey) {
|
||||
if (position == 0)
|
||||
@@ -149,6 +154,7 @@ class UITextBox extends UISliceSprite implements IUIFocusable {
|
||||
}
|
||||
|
||||
changeSelection(-1);
|
||||
return;
|
||||
case RIGHT:
|
||||
if (modifier.ctrlKey) {
|
||||
if (position == label.text.length)
|
||||
@@ -165,6 +171,7 @@ class UITextBox extends UISliceSprite implements IUIFocusable {
|
||||
}
|
||||
|
||||
changeSelection(1);
|
||||
return;
|
||||
case BACKSPACE:
|
||||
UIState.playEditorSound(Flags.DEFAULT_EDITOR_TEXTREMOVE_SOUND);
|
||||
|
||||
@@ -181,6 +188,7 @@ class UITextBox extends UISliceSprite implements IUIFocusable {
|
||||
label.text = label.text.substr(0, position - 1) + label.text.substr(position);
|
||||
changeSelection(-1);
|
||||
}
|
||||
return;
|
||||
case DELETE:
|
||||
UIState.playEditorSound(Flags.DEFAULT_EDITOR_TEXTREMOVE_SOUND);
|
||||
|
||||
@@ -196,48 +204,46 @@ class UITextBox extends UISliceSprite implements IUIFocusable {
|
||||
if (position < label.text.length) {
|
||||
label.text = label.text.substr(0, position) + label.text.substr(position + 1);
|
||||
}
|
||||
return;
|
||||
case HOME:
|
||||
position = 0;
|
||||
return;
|
||||
case END:
|
||||
position = label.text.length;
|
||||
return;
|
||||
case V:
|
||||
UIState.playEditorSound(Flags.DEFAULT_EDITOR_PASTE_SOUND);
|
||||
// Hey lj here, fixed copying because before we checked if the modifier was left or right ctrl
|
||||
// but somehow it gave a int outside of the KeyModifier's range :sob:
|
||||
// apparently there is a boolean that just checks for you. yw :D
|
||||
if (modifier.ctrlKey) {
|
||||
UIState.playEditorSound(Flags.DEFAULT_EDITOR_PASTE_SOUND);
|
||||
|
||||
// if we are not holding ctrl, ignore
|
||||
if (!modifier.ctrlKey)
|
||||
return;
|
||||
// we pasting
|
||||
var data:String = Clipboard.generalClipboard.getData(TEXT_FORMAT);
|
||||
if (data != null)
|
||||
onTextInput(data);
|
||||
// Hey lj here, fixed copying because before we checked if the modifier was left or right ctrl
|
||||
// but somehow it gave a int outside of the KeyModifier's range :sob:
|
||||
// apparently there is a boolean that just checks for you. yw :D
|
||||
|
||||
// we pasting
|
||||
var data:String = Clipboard.generalClipboard.getData(TEXT_FORMAT);
|
||||
if (data != null)
|
||||
onTextInput(data);
|
||||
}
|
||||
case C:
|
||||
UIState.playEditorSound(Flags.DEFAULT_EDITOR_COPY_SOUND);
|
||||
// if we are not holding ctrl, ignore
|
||||
if (!modifier.ctrlKey)
|
||||
return;
|
||||
if (modifier.ctrlKey) {
|
||||
UIState.playEditorSound(Flags.DEFAULT_EDITOR_COPY_SOUND);
|
||||
|
||||
// copying
|
||||
Clipboard.generalClipboard.setData(TEXT_FORMAT, label.text);
|
||||
Clipboard.generalClipboard.setData(TEXT_FORMAT, label.text);
|
||||
}
|
||||
case X:
|
||||
UIState.playEditorSound(Flags.DEFAULT_EDITOR_CUT_SOUND);
|
||||
if (modifier.ctrlKey) {
|
||||
UIState.playEditorSound(Flags.DEFAULT_EDITOR_CUT_SOUND);
|
||||
|
||||
// if we are not holding ctrl, ignore
|
||||
if (!modifier.ctrlKey)
|
||||
Clipboard.generalClipboard.setData(TEXT_FORMAT, label.text);
|
||||
position = 0;
|
||||
label.text = "";
|
||||
return;
|
||||
|
||||
// cutting
|
||||
Clipboard.generalClipboard.setData(TEXT_FORMAT, label.text);
|
||||
position = 0;
|
||||
label.text = "";
|
||||
}
|
||||
default:
|
||||
if (modifier.ctrlKey || modifier.altKey || modifier.shiftKey)
|
||||
return;
|
||||
|
||||
UIState.playEditorSound(Flags.DEFAULT_EDITOR_TEXTTYPE_SOUND);
|
||||
}
|
||||
|
||||
if (modifier.ctrlKey || modifier.altKey || modifier.shiftKey) return;
|
||||
UIState.playEditorSound(Flags.DEFAULT_EDITOR_TEXTTYPE_SOUND);
|
||||
}
|
||||
|
||||
public function changeSelection(change:Int) {
|
||||
|
||||
@@ -1102,7 +1102,7 @@ class PlayState extends MusicBeatState
|
||||
if (notNull) PlayState.instance.gameAndCharsCall("onStageDestroy", [stage]);
|
||||
scripts.call("destroy");
|
||||
|
||||
for (g in __cachedGraphics) g.useCount--;
|
||||
for (g in __cachedGraphics) g.decrementUseCount();
|
||||
@:privateAccess {
|
||||
for (strumLine in strumLines.members) FlxG.sound.destroySound(strumLine.vocals);
|
||||
if (FlxG.sound.music != inst) FlxG.sound.destroySound(inst);
|
||||
@@ -1251,11 +1251,6 @@ class PlayState extends MusicBeatState
|
||||
@:dox(hide)
|
||||
override public function onFocus():Void
|
||||
{
|
||||
if (!paused && FlxG.autoPause) {
|
||||
for (strumLine in strumLines.members) strumLine.vocals.resume();
|
||||
inst.resume();
|
||||
vocals.resume();
|
||||
}
|
||||
gameAndCharsCall("onFocus");
|
||||
updateDiscordPresence();
|
||||
super.onFocus();
|
||||
@@ -1264,11 +1259,6 @@ class PlayState extends MusicBeatState
|
||||
@:dox(hide)
|
||||
override public function onFocusLost():Void
|
||||
{
|
||||
if (!paused && FlxG.autoPause) {
|
||||
for (strumLine in strumLines.members) strumLine.vocals.pause();
|
||||
inst.pause();
|
||||
vocals.pause();
|
||||
}
|
||||
gameAndCharsCall("onFocusLost");
|
||||
updateDiscordPresence();
|
||||
super.onFocusLost();
|
||||
|
||||
@@ -60,15 +60,12 @@ class VideoCutscene extends Cutscene {
|
||||
|
||||
add(video = new FlxVideoSprite());
|
||||
video.antialiasing = true;
|
||||
#if (hxvlc < version("2.0.0"))
|
||||
video.autoPause = false; // Imma handle it better inside this class, mainly because of the pause menu - Nex
|
||||
#end
|
||||
video.bitmap.onEndReached.add(close);
|
||||
video.bitmap.onFormatSetup.add(function() if (video.bitmap != null && video.bitmap.bitmapData != null) {
|
||||
final width = video.bitmap.bitmapData.width;
|
||||
final height = video.bitmap.bitmapData.height;
|
||||
final scale:Float = Math.min(FlxG.width / width, FlxG.height / height);
|
||||
video.setGraphicSize(Std.int(width * scale), Std.int(height * scale));
|
||||
video.setGraphicSize(width * scale, height * scale);
|
||||
video.updateHitbox();
|
||||
video.screenCenter();
|
||||
});
|
||||
@@ -103,7 +100,7 @@ class VideoCutscene extends Cutscene {
|
||||
FlxTween.tween(loadingBackdrop, {alpha: 1}, 0.5, {ease: FlxEase.sineInOut});
|
||||
|
||||
Main.execAsync(function() {
|
||||
if (video.load(localPath)) new FlxTimer().start(0.001, function(_) {
|
||||
if (video.load(localPath)) FlxTimer.wait(0.001, function() {
|
||||
mutex.acquire(); onReady(); mutex.release();
|
||||
});
|
||||
else { mutex.acquire(); close(); mutex.release(); }
|
||||
@@ -203,18 +200,6 @@ class VideoCutscene extends Cutscene {
|
||||
}
|
||||
}
|
||||
|
||||
#if (hxvlc < version("2.0.0"))
|
||||
@:dox(hide) override public function onFocus() {
|
||||
if(FlxG.autoPause && !paused) video.resume();
|
||||
super.onFocus();
|
||||
}
|
||||
|
||||
@:dox(hide) override public function onFocusLost() {
|
||||
if(FlxG.autoPause && !paused) video.pause();
|
||||
super.onFocusLost();
|
||||
}
|
||||
#end
|
||||
|
||||
public override function pauseCutscene() {
|
||||
video.pause();
|
||||
super.pauseCutscene();
|
||||
|
||||
@@ -189,37 +189,6 @@ class FreeplayState extends MusicBeatState
|
||||
interpColor = new FlxInterpolateColor(bg.color);
|
||||
}
|
||||
|
||||
#if PRELOAD_ALL
|
||||
/**
|
||||
* How much time a song stays selected until it autoplays.
|
||||
*/
|
||||
public var timeUntilAutoplay:Float = 1;
|
||||
/**
|
||||
* Whenever the song autoplays when hovered over.
|
||||
*/
|
||||
public var disableAutoPlay:Bool = false;
|
||||
/**
|
||||
* Whenever the autoplayed song gets async loaded.
|
||||
*/
|
||||
public var disableAsyncLoading:Bool = #if desktop false #else true #end;
|
||||
/**
|
||||
* Time elapsed since last autoplay. If this time exceeds `timeUntilAutoplay`, the currently selected song will play.
|
||||
*/
|
||||
public var autoplayElapsed:Float = 0;
|
||||
/**
|
||||
* Whenever the currently selected song instrumental is playing.
|
||||
*/
|
||||
public var songInstPlaying:Bool = true;
|
||||
/**
|
||||
* Path to the currently playing song instrumental.
|
||||
*/
|
||||
public var curPlayingInst:String = null;
|
||||
/**
|
||||
* If it should play the song automatically.
|
||||
*/
|
||||
public var autoplayShouldPlay:Bool = true;
|
||||
#end
|
||||
|
||||
private var TEXT_FREEPLAY_SCORE = TU.getRaw("freeplay.score");
|
||||
|
||||
override function update(elapsed:Float)
|
||||
@@ -255,46 +224,6 @@ class FreeplayState extends MusicBeatState
|
||||
interpColor.fpsLerpTo(curSong.color, 0.0625);
|
||||
bg.color = interpColor.color;
|
||||
|
||||
#if PRELOAD_ALL
|
||||
var dontPlaySongThisFrame = false;
|
||||
autoplayElapsed += elapsed;
|
||||
if (!disableAutoPlay && !songInstPlaying && (autoplayElapsed > timeUntilAutoplay)) {
|
||||
if (curPlayingInst != (curPlayingInst = Paths.inst(curSong.name, curDifficulties[curDifficulty], curSong.instSuffix))) {
|
||||
var streamed = false;
|
||||
/*if (Options.streamedMusic) {
|
||||
var sound = Assets.getMusic(curPlayingInst, true, false);
|
||||
streamed = sound != null;
|
||||
|
||||
if (streamed && autoplayShouldPlay) {
|
||||
FlxG.sound.playMusic(sound, 0);
|
||||
Conductor.changeBPM(curSong.bpm, curSong.beatsPerMeasure, curSong.stepsPerBeat);
|
||||
}
|
||||
}*/
|
||||
|
||||
if (!streamed) {
|
||||
var huh:Void->Void = function() {
|
||||
var soundPath = curPlayingInst;
|
||||
var sound = null;
|
||||
if (Assets.exists(soundPath, SOUND) || Assets.exists(soundPath, MUSIC))
|
||||
sound = Assets.getSound(soundPath);
|
||||
else
|
||||
FlxG.log.error('Could not find a Sound asset with an ID of \'$soundPath\'.');
|
||||
|
||||
if (sound != null && autoplayShouldPlay) {
|
||||
FlxG.sound.playMusic(sound, 0);
|
||||
Conductor.changeBPM(curSong.bpm, curSong.beatsPerMeasure, curSong.stepsPerBeat);
|
||||
}
|
||||
}
|
||||
if (!disableAsyncLoading) Main.execAsync(huh);
|
||||
else huh();
|
||||
}
|
||||
}
|
||||
songInstPlaying = true;
|
||||
if (disableAsyncLoading/* && !Options.streamedMusic*/) dontPlaySongThisFrame = true;
|
||||
}
|
||||
#end
|
||||
|
||||
|
||||
if (controls.BACK)
|
||||
{
|
||||
CoolUtil.playMenuSFX(CANCEL, 0.7);
|
||||
@@ -306,7 +235,7 @@ class FreeplayState extends MusicBeatState
|
||||
convertChart();
|
||||
#end
|
||||
|
||||
if (controls.ACCEPT #if PRELOAD_ALL && !dontPlaySongThisFrame #end)
|
||||
if (controls.ACCEPT)
|
||||
select();
|
||||
}
|
||||
|
||||
@@ -338,10 +267,6 @@ class FreeplayState extends MusicBeatState
|
||||
|
||||
if (event.cancelled) return;
|
||||
|
||||
#if PRELOAD_ALL
|
||||
autoplayShouldPlay = false;
|
||||
#end
|
||||
|
||||
Options.freeplayLastSong = curSong.name;
|
||||
Options.freeplayLastDifficulty = curDifficulties[curDifficulty];
|
||||
Options.freeplayLastVariation = curSong.variant;
|
||||
@@ -377,13 +302,6 @@ class FreeplayState extends MusicBeatState
|
||||
updateCurSong();
|
||||
updateScore();
|
||||
|
||||
#if PRELOAD_ALL
|
||||
if (curSong != prevSong) {
|
||||
autoplayElapsed = 0;
|
||||
songInstPlaying = false;
|
||||
}
|
||||
#end
|
||||
|
||||
var text = validDifficulties ? curDifficulties[curDifficulty].toUpperCase() + (curSong != songs[curSelected] ? ' (${curSong.variant.toUpperCase()})' : '') : '-';
|
||||
diffText.text = curDifficulties.length > 1 ? '< $text >' : text;
|
||||
}
|
||||
@@ -466,11 +384,6 @@ class FreeplayState extends MusicBeatState
|
||||
|
||||
changeDiff(0, true);
|
||||
|
||||
#if PRELOAD_ALL
|
||||
autoplayElapsed = 0;
|
||||
songInstPlaying = false;
|
||||
#end
|
||||
|
||||
coopText.visible = curSong.coopAllowed || curSong.opponentModeAllowed;
|
||||
}
|
||||
|
||||
@@ -478,8 +391,8 @@ class FreeplayState extends MusicBeatState
|
||||
var event = event("onUpdateOptionsAlpha", EventManager.get(FreeplayAlphaUpdateEvent).recycle(0.6, 0.45, 1, 1, 0.25));
|
||||
if (event.cancelled) return;
|
||||
|
||||
final idleAlpha = #if PRELOAD_ALL songInstPlaying ? event.idlePlayingAlpha : #end event.idleAlpha;
|
||||
final selectedAlpha = #if PRELOAD_ALL songInstPlaying ? event.selectedPlayingAlpha : #end event.selectedAlpha;
|
||||
final idleAlpha = event.idleAlpha;
|
||||
final selectedAlpha = event.selectedAlpha;
|
||||
|
||||
for (i in 0...iconArray.length)
|
||||
iconArray[i].alpha = lerp(iconArray[i].alpha, idleAlpha, event.lerp);
|
||||
|
||||
@@ -43,8 +43,8 @@ class Options
|
||||
public static var framerate:Int = 120;
|
||||
public static var gpuOnlyBitmaps:Bool = #if (mac || web) false #else true #end; // causes issues on mac and web
|
||||
public static var language = "en"; // default to english, Flags.DEFAULT_LANGUAGE should not modify this
|
||||
public static var streamedMusic:Bool = false;
|
||||
public static var streamedVocals:Bool = false;
|
||||
public static var streamedMusic:Bool = true;
|
||||
public static var streamedVocals:Bool = true;
|
||||
public static var quality:Int = 1;
|
||||
public static var allowConfigWarning:Bool = true;
|
||||
#if MODCHARTING_FEATURES
|
||||
@@ -229,6 +229,7 @@ class Options
|
||||
applyKeybinds();
|
||||
applyQuality();
|
||||
|
||||
flixel.sound.FlxSoundData.allowStreaming = streamedMusic;
|
||||
FlxG.sound.defaultMusicGroup.volume = volumeMusic;
|
||||
FlxG.autoPause = autoPause;
|
||||
if (FlxG.updateFramerate < framerate) FlxG.drawFramerate = FlxG.updateFramerate = framerate;
|
||||
|
||||
@@ -67,14 +67,7 @@ class AdvancedGameplayOptions extends TreeMenuScreen {
|
||||
public function new() {
|
||||
super('optionsMenu.advanced', 'optionsTree.gameplay.advanced-desc', 'GameplayOptions.Advanced.');
|
||||
|
||||
// Remove locked whenever this PR from FunkinCrew is merged.
|
||||
// https://github.com/FunkinCrew/lime/pull/57
|
||||
for (checkbox in [
|
||||
new Checkbox(getNameID('streamedMusic'), getDescID('streamedMusic'), 'streamedMusic'),
|
||||
new Checkbox(getNameID('streamedVocals'), getDescID('streamedVocals'), 'streamedVocals')
|
||||
]) {
|
||||
checkbox.locked = true;
|
||||
add(checkbox);
|
||||
}
|
||||
add(new Checkbox(getNameID('streamedMusic'), getDescID('streamedMusic'), 'streamedMusic'));
|
||||
add(new Checkbox(getNameID('streamedVocals'), getDescID('streamedVocals'), 'streamedVocals'));
|
||||
}
|
||||
}
|
||||
@@ -1,297 +0,0 @@
|
||||
package haxe;
|
||||
|
||||
#if !lime_cffi
|
||||
// Original haxe.Timer class
|
||||
|
||||
/*
|
||||
* Copyright (C)2005-2018 Haxe Foundation
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
/**
|
||||
The Timer class allows you to create asynchronous timers on platforms that
|
||||
support events.
|
||||
|
||||
The intended usage is to create an instance of the Timer class with a given
|
||||
interval, set its run() method to a custom function to be invoked and
|
||||
eventually call stop() to stop the Timer.
|
||||
|
||||
Note that a running Timer may or may not prevent the program to exit
|
||||
automatically when main() returns.
|
||||
|
||||
It is also possible to extend this class and override its run() method in
|
||||
the child class.
|
||||
**/
|
||||
class Timer
|
||||
{
|
||||
#if (flash || js)
|
||||
private var id:Null<Int>;
|
||||
#elseif java
|
||||
private var timer:java.util.Timer;
|
||||
private var task:java.util.TimerTask;
|
||||
#elseif (haxe_ver >= "3.4.0")
|
||||
private var event:MainLoop.MainEvent;
|
||||
#end
|
||||
|
||||
/**
|
||||
Creates a new timer that will run every `time_ms` milliseconds.
|
||||
|
||||
After creating the Timer instance, it calls `this.run` repeatedly,
|
||||
with delays of `time_ms` milliseconds, until `this.stop` is called.
|
||||
|
||||
The first invocation occurs after `time_ms` milliseconds, not
|
||||
immediately.
|
||||
|
||||
The accuracy of this may be platform-dependent.
|
||||
**/
|
||||
public function new(time_ms:Int)
|
||||
{
|
||||
#if flash
|
||||
var me = this;
|
||||
id = untyped __global__["flash.utils.setInterval"](function()
|
||||
{
|
||||
me.run();
|
||||
}, time_ms);
|
||||
#elseif js
|
||||
var me = this;
|
||||
id = untyped setInterval(function() me.run(), time_ms);
|
||||
#elseif java
|
||||
timer = new java.util.Timer();
|
||||
timer.scheduleAtFixedRate(task = new TimerTask(this), haxe.Int64.ofInt(time_ms), haxe.Int64.ofInt(time_ms));
|
||||
#elseif (haxe_ver >= "3.4.0")
|
||||
var dt = time_ms / 1000;
|
||||
event = MainLoop.add(function()
|
||||
{
|
||||
@:privateAccess event.nextRun += dt;
|
||||
run();
|
||||
});
|
||||
event.delay(dt);
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
Stops `this` Timer.
|
||||
|
||||
After calling this method, no additional invocations of `this.run`
|
||||
will occur.
|
||||
|
||||
It is not possible to restart `this` Timer once stopped.
|
||||
**/
|
||||
public function stop()
|
||||
{
|
||||
#if (flash || js)
|
||||
if (id == null) return;
|
||||
#if flash
|
||||
untyped __global__["flash.utils.clearInterval"](id);
|
||||
#elseif js
|
||||
untyped clearInterval(id);
|
||||
#end
|
||||
id = null;
|
||||
#elseif java
|
||||
if (timer != null)
|
||||
{
|
||||
timer.cancel();
|
||||
timer = null;
|
||||
}
|
||||
task = null;
|
||||
#elseif (haxe_ver >= "3.4.0")
|
||||
if (event != null)
|
||||
{
|
||||
event.stop();
|
||||
event = null;
|
||||
}
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
This method is invoked repeatedly on `this` Timer.
|
||||
|
||||
It can be overridden in a subclass, or rebound directly to a custom
|
||||
function:
|
||||
var timer = new haxe.Timer(1000); // 1000ms delay
|
||||
timer.run = function() { ... }
|
||||
|
||||
Once bound, it can still be rebound to different functions until `this`
|
||||
Timer is stopped through a call to `this.stop`.
|
||||
**/
|
||||
public dynamic function run() {}
|
||||
|
||||
/**
|
||||
Invokes `f` after `time_ms` milliseconds.
|
||||
|
||||
This is a convenience function for creating a new Timer instance with
|
||||
`time_ms` as argument, binding its run() method to `f` and then stopping
|
||||
`this` Timer upon the first invocation.
|
||||
|
||||
If `f` is null, the result is unspecified.
|
||||
**/
|
||||
public static function delay(f:Void->Void, time_ms:Int)
|
||||
{
|
||||
var t = new haxe.Timer(time_ms);
|
||||
t.run = function()
|
||||
{
|
||||
t.stop();
|
||||
f();
|
||||
};
|
||||
return t;
|
||||
}
|
||||
|
||||
/**
|
||||
Measures the time it takes to execute `f`, in seconds with fractions.
|
||||
|
||||
This is a convenience function for calculating the difference between
|
||||
Timer.stamp() before and after the invocation of `f`.
|
||||
|
||||
The difference is passed as argument to Log.trace(), with "s" appended
|
||||
to denote the unit. The optional `pos` argument is passed through.
|
||||
|
||||
If `f` is null, the result is unspecified.
|
||||
**/
|
||||
public static function measure<T>(f:Void->T, ?pos:PosInfos):T
|
||||
{
|
||||
var t0 = stamp();
|
||||
var r = f();
|
||||
Log.trace((stamp() - t0) + "s", pos);
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
Returns a timestamp, in seconds with fractions.
|
||||
|
||||
The value itself might differ depending on platforms, only differences
|
||||
between two values make sense.
|
||||
**/
|
||||
public static inline function stamp():Float
|
||||
{
|
||||
#if flash
|
||||
return flash.Lib.getTimer() / 1000;
|
||||
#elseif (neko || php)
|
||||
return Sys.time();
|
||||
#elseif js
|
||||
return Date.now().getTime() / 1000;
|
||||
#elseif cpp
|
||||
return untyped __global__.__time_stamp();
|
||||
#elseif python
|
||||
return Sys.cpuTime();
|
||||
#elseif sys
|
||||
return Sys.time();
|
||||
#else
|
||||
return 0;
|
||||
#end
|
||||
}
|
||||
}
|
||||
|
||||
#if java
|
||||
@:nativeGen
|
||||
private class TimerTask extends java.util.TimerTask
|
||||
{
|
||||
var timer:Timer;
|
||||
|
||||
public function new(timer:Timer):Void
|
||||
{
|
||||
super();
|
||||
this.timer = timer;
|
||||
}
|
||||
|
||||
@:overload override public function run():Void
|
||||
{
|
||||
timer.run();
|
||||
}
|
||||
}
|
||||
#end
|
||||
#else
|
||||
import lime.system.System;
|
||||
|
||||
class Timer
|
||||
{
|
||||
private static var sRunningTimers:Array<Timer> = [];
|
||||
|
||||
private var mTime:Float;
|
||||
private var mFireAt:Float;
|
||||
private var mRunning:Bool;
|
||||
|
||||
public function new(time:Float)
|
||||
{
|
||||
mTime = time;
|
||||
sRunningTimers.push(this);
|
||||
mFireAt = getMS() + mTime;
|
||||
mRunning = true;
|
||||
}
|
||||
|
||||
public static function delay(f:Void->Void, time:Int)
|
||||
{
|
||||
var t = new Timer(time);
|
||||
|
||||
t.run = function()
|
||||
{
|
||||
t.stop();
|
||||
f();
|
||||
};
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
private static function getMS():Float
|
||||
{
|
||||
return System.getTimer();
|
||||
}
|
||||
|
||||
public static function measure<T>(f:Void->T, ?pos:PosInfos):T
|
||||
{
|
||||
var t0 = stamp();
|
||||
var r = f();
|
||||
Log.trace((stamp() - t0) + "s", pos);
|
||||
return r;
|
||||
}
|
||||
|
||||
dynamic public function run() {}
|
||||
|
||||
public static inline function stamp():Float
|
||||
{
|
||||
var timer = System.getTimer();
|
||||
return (timer > 0 ? timer / 1000 : 0);
|
||||
}
|
||||
|
||||
public function stop():Void
|
||||
{
|
||||
/*if (mRunning)
|
||||
{
|
||||
|
||||
for (i in 0...sRunningTimers.length)
|
||||
{
|
||||
if (sRunningTimers[i] == this)
|
||||
{
|
||||
sRunningTimers[i] = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}*/
|
||||
mRunning = false;
|
||||
}
|
||||
|
||||
@:noCompletion private function __check(inTime:Float)
|
||||
{
|
||||
if (inTime >= mFireAt)
|
||||
{
|
||||
mFireAt += mTime;
|
||||
run();
|
||||
}
|
||||
}
|
||||
}
|
||||
#end
|
||||
@@ -1,283 +0,0 @@
|
||||
package lime._internal.backend.html5;
|
||||
|
||||
import lime.math.Vector4;
|
||||
import lime.media.AudioSource;
|
||||
import lime.media.AudioManager;
|
||||
|
||||
@:access(lime.media.AudioBuffer)
|
||||
class HTML5AudioSource
|
||||
{
|
||||
private var completed:Bool;
|
||||
private var gain:Float;
|
||||
private var id:Int;
|
||||
private var length:Null<Float>;
|
||||
private var loops:Int;
|
||||
private var parent:AudioSource;
|
||||
private var playing:Bool;
|
||||
private var position:Vector4;
|
||||
|
||||
public function new(parent:AudioSource)
|
||||
{
|
||||
this.parent = parent;
|
||||
|
||||
id = -1;
|
||||
gain = 1;
|
||||
position = new Vector4();
|
||||
}
|
||||
|
||||
public function dispose():Void {
|
||||
stop();
|
||||
}
|
||||
|
||||
public function init():Void {}
|
||||
|
||||
public function play():Void
|
||||
{
|
||||
#if lime_howlerjs
|
||||
if (playing || parent.buffer == null || parent.buffer.__srcHowl == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
playing = true;
|
||||
|
||||
var time = getCurrentTime();
|
||||
|
||||
completed = false;
|
||||
|
||||
var cacheVolume = untyped parent.buffer.__srcHowl._volume;
|
||||
untyped parent.buffer.__srcHowl._volume = parent.gain;
|
||||
|
||||
id = parent.buffer.__srcHowl.play();
|
||||
|
||||
untyped parent.buffer.__srcHowl._volume = cacheVolume;
|
||||
// setGain (parent.gain);
|
||||
|
||||
setPosition(parent.position);
|
||||
|
||||
parent.buffer.__srcHowl.on("end", howl_onEnd, id);
|
||||
|
||||
// Calling setCurrentTime causes html5 audio to replay from this position on next frame
|
||||
#if force_html5_audio
|
||||
if (time == 0) setCurrentTime(time);
|
||||
#else
|
||||
setCurrentTime(time);
|
||||
#end
|
||||
#end
|
||||
}
|
||||
|
||||
public function pause():Void
|
||||
{
|
||||
#if lime_howlerjs
|
||||
playing = false;
|
||||
|
||||
if (parent.buffer != null && parent.buffer.__srcHowl != null)
|
||||
{
|
||||
parent.buffer.__srcHowl.pause(id);
|
||||
}
|
||||
#end
|
||||
}
|
||||
|
||||
public function stop():Void
|
||||
{
|
||||
#if lime_howlerjs
|
||||
playing = false;
|
||||
|
||||
if (parent.buffer != null && parent.buffer.__srcHowl != null)
|
||||
{
|
||||
parent.buffer.__srcHowl.stop(id);
|
||||
parent.buffer.__srcHowl.off("end", howl_onEnd, id);
|
||||
}
|
||||
#end
|
||||
}
|
||||
|
||||
// Event Handlers
|
||||
private function howl_onEnd()
|
||||
{
|
||||
#if lime_howlerjs
|
||||
playing = false;
|
||||
|
||||
if (loops > 0)
|
||||
{
|
||||
loops--;
|
||||
stop();
|
||||
if (loopTime != null && loopTime > 0) setCurrentTime(loopTime);
|
||||
play();
|
||||
parent.onLoop.dispatch();
|
||||
return;
|
||||
}
|
||||
else if (parent.buffer != null && parent.buffer.__srcHowl != null)
|
||||
{
|
||||
parent.buffer.__srcHowl.stop(id);
|
||||
parent.buffer.__srcHowl.off("end", howl_onEnd, id);
|
||||
}
|
||||
|
||||
completed = true;
|
||||
parent.onComplete.dispatch();
|
||||
#end
|
||||
}
|
||||
|
||||
// Get & Set Methods
|
||||
public function getCurrentTime():Float
|
||||
{
|
||||
if (id == -1)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if lime_howlerjs
|
||||
if (completed)
|
||||
{
|
||||
return getLength();
|
||||
}
|
||||
else if (parent.buffer != null && parent.buffer.__srcHowl != null)
|
||||
{
|
||||
var time = parent.buffer.__srcHowl.seek(id) * 1000.0 - parent.offset;
|
||||
if (time < 0) return 0;
|
||||
return time;
|
||||
}
|
||||
#end
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public function getLatency():Float
|
||||
{
|
||||
var ctx = AudioManager.context.web;
|
||||
if (ctx != null)
|
||||
{
|
||||
var baseLatency:Float = untyped ctx.baseLatency != null ? untyped ctx.baseLatency : 0;
|
||||
var outputLatency:Float = untyped ctx.outputLatency != null ? untyped ctx.outputLatency : 0;
|
||||
|
||||
return (baseLatency + outputLatency) * 1000;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public function setCurrentTime(value:Float):Float
|
||||
{
|
||||
#if lime_howlerjs
|
||||
if (parent.buffer != null && parent.buffer.__srcHowl != null)
|
||||
{
|
||||
// if (playing) buffer.__srcHowl.play (id);
|
||||
var pos = (value + parent.offset) / 1000;
|
||||
if (pos < 0) pos = 0;
|
||||
parent.buffer.__srcHowl.seek(pos, id);
|
||||
}
|
||||
#end
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public function getGain():Float
|
||||
{
|
||||
return gain;
|
||||
}
|
||||
|
||||
public function setGain(value:Float):Float
|
||||
{
|
||||
#if lime_howlerjs
|
||||
// set howler volume only if we have an active id.
|
||||
// Passing -1 might create issues in future play()'s.
|
||||
|
||||
if (parent.buffer != null && parent.buffer.__srcHowl != null && id != -1)
|
||||
{
|
||||
parent.buffer.__srcHowl.volume(value, id);
|
||||
}
|
||||
#end
|
||||
|
||||
return gain = value;
|
||||
}
|
||||
|
||||
public function getLength():Null<Float>
|
||||
{
|
||||
if (length != 0)
|
||||
{
|
||||
return length;
|
||||
}
|
||||
|
||||
#if lime_howlerjs
|
||||
if (parent.buffer != null && parent.buffer.__srcHowl != null)
|
||||
{
|
||||
return parent.buffer.__srcHowl.duration() * 1000.0;
|
||||
}
|
||||
#end
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public function setLength(value:Null<Float>):Null<Float>
|
||||
{
|
||||
return length = value;
|
||||
}
|
||||
|
||||
public function getLoops():Int
|
||||
{
|
||||
return loops;
|
||||
}
|
||||
|
||||
public function setLoops(value:Int):Int
|
||||
{
|
||||
return loops = value;
|
||||
}
|
||||
|
||||
public function getLoopTime():Float {
|
||||
return loopTime;
|
||||
}
|
||||
|
||||
public function setLoopTime(value:Float):Float {
|
||||
return loopTime = value;
|
||||
}
|
||||
|
||||
public function getPitch():Float
|
||||
{
|
||||
#if lime_howlerjs
|
||||
return parent.buffer.__srcHowl.rate();
|
||||
#else
|
||||
return 1;
|
||||
#end
|
||||
}
|
||||
|
||||
public function setPitch(value:Float):Float
|
||||
{
|
||||
#if lime_howlerjs
|
||||
parent.buffer.__srcHowl.rate(value);
|
||||
#end
|
||||
|
||||
return getPitch();
|
||||
}
|
||||
|
||||
|
||||
public function getPosition():Vector4
|
||||
{
|
||||
return position;
|
||||
}
|
||||
|
||||
public function setPosition(value:Vector4):Vector4
|
||||
{
|
||||
position.x = value.x;
|
||||
position.y = value.y;
|
||||
position.z = value.z;
|
||||
position.w = value.w;
|
||||
|
||||
#if lime_howlerjs
|
||||
if (parent.buffer != null && parent.buffer.__srcHowl != null && parent.buffer.__srcHowl.pos != null) parent.buffer.__srcHowl.pos(position.x, position.y, position.z, id);
|
||||
// There are more settings to the position of the sound on the "pannerAttr()" function of howler. Maybe somebody who understands sound should look into it?
|
||||
#end
|
||||
|
||||
return position;
|
||||
}
|
||||
|
||||
public function getPan():Float
|
||||
{
|
||||
return position.x;
|
||||
}
|
||||
|
||||
public function setPan(value:Float):Float
|
||||
{
|
||||
position.setTo(value, 0, -Math.sqrt(1 - value * value));
|
||||
if (parent.buffer != null && parent.buffer.__srcHowl != null && parent.buffer.__srcHowl.stereo != null) parent.buffer.__srcHowl.stereo(value, id);
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@@ -1,981 +0,0 @@
|
||||
package lime._internal.backend.native;
|
||||
|
||||
import haxe.Timer;
|
||||
import lime._internal.backend.native.NativeCFFI;
|
||||
import lime.app.Application;
|
||||
import lime.graphics.opengl.GL;
|
||||
import lime.graphics.OpenGLRenderContext;
|
||||
import lime.graphics.RenderContext;
|
||||
import lime.math.Rectangle;
|
||||
import lime.media.AudioManager;
|
||||
import lime.system.Clipboard;
|
||||
import lime.system.Display;
|
||||
import lime.system.DisplayMode;
|
||||
import lime.system.JNI;
|
||||
import lime.system.Sensor;
|
||||
import lime.system.SensorType;
|
||||
import lime.system.System;
|
||||
import lime.ui.Gamepad;
|
||||
import lime.ui.Joystick;
|
||||
import lime.ui.JoystickHatPosition;
|
||||
import lime.ui.KeyCode;
|
||||
import lime.ui.KeyModifier;
|
||||
import lime.ui.Touch;
|
||||
import lime.ui.Window;
|
||||
|
||||
#if !lime_debug
|
||||
@:fileXml('tags="haxe,release"')
|
||||
@:noDebug
|
||||
#end
|
||||
@:access(haxe.Timer)
|
||||
@:access(lime._internal.backend.native.NativeCFFI)
|
||||
@:access(lime._internal.backend.native.NativeOpenGLRenderContext)
|
||||
@:access(lime._internal.backend.native.NativeWindow)
|
||||
@:access(lime.app.Application)
|
||||
@:access(lime.graphics.opengl.GL)
|
||||
@:access(lime.graphics.OpenGLRenderContext)
|
||||
@:access(lime.graphics.Renderer)
|
||||
@:access(lime.system.Clipboard)
|
||||
@:access(lime.system.Sensor)
|
||||
@:access(lime.ui.Gamepad)
|
||||
@:access(lime.ui.Joystick)
|
||||
@:access(lime.ui.Window)
|
||||
class NativeApplication
|
||||
{
|
||||
private var applicationEventInfo = new ApplicationEventInfo(UPDATE);
|
||||
private var clipboardEventInfo = new ClipboardEventInfo();
|
||||
private var currentTouches = new Map<Int, Touch>();
|
||||
private var dropEventInfo = new DropEventInfo();
|
||||
private var gamepadEventInfo = new GamepadEventInfo();
|
||||
private var joystickEventInfo = new JoystickEventInfo();
|
||||
private var keyEventInfo = new KeyEventInfo();
|
||||
private var mouseEventInfo = new MouseEventInfo();
|
||||
private var renderEventInfo = new RenderEventInfo(RENDER);
|
||||
private var sensorEventInfo = new SensorEventInfo();
|
||||
private var textEventInfo = new TextEventInfo();
|
||||
private var touchEventInfo = new TouchEventInfo();
|
||||
private var unusedTouchesPool = new List<Touch>();
|
||||
private var windowEventInfo = new WindowEventInfo();
|
||||
|
||||
public var handle:Dynamic;
|
||||
|
||||
private var pauseTimer:Int;
|
||||
private var parent:Application;
|
||||
private var toggleFullscreen:Bool;
|
||||
|
||||
private static function __init__()
|
||||
{
|
||||
#if (lime_cffi && !macro)
|
||||
var init = NativeCFFI;
|
||||
#end
|
||||
}
|
||||
|
||||
public function new(parent:Application):Void
|
||||
{
|
||||
this.parent = parent;
|
||||
pauseTimer = -1;
|
||||
toggleFullscreen = true;
|
||||
|
||||
AudioManager.init();
|
||||
|
||||
#if (ios || android || tvos)
|
||||
Sensor.registerSensor(SensorType.ACCELEROMETER, 0);
|
||||
#end
|
||||
|
||||
#if (!macro && lime_cffi)
|
||||
handle = NativeCFFI.lime_application_create();
|
||||
#end
|
||||
}
|
||||
|
||||
private function advanceTimer():Void
|
||||
{
|
||||
#if lime_cffi
|
||||
if (pauseTimer > -1)
|
||||
{
|
||||
var offset = System.getTimer() - pauseTimer;
|
||||
for(timer in Timer.sRunningTimers) {
|
||||
if(timer != null && timer.mRunning) timer.mFireAt += offset;
|
||||
}
|
||||
pauseTimer = -1;
|
||||
}
|
||||
#end
|
||||
}
|
||||
|
||||
public function exec():Int
|
||||
{
|
||||
#if !macro
|
||||
#if lime_cffi
|
||||
NativeCFFI.lime_application_event_manager_register(handleApplicationEvent, applicationEventInfo);
|
||||
NativeCFFI.lime_clipboard_event_manager_register(handleClipboardEvent, clipboardEventInfo);
|
||||
NativeCFFI.lime_drop_event_manager_register(handleDropEvent, dropEventInfo);
|
||||
NativeCFFI.lime_gamepad_event_manager_register(handleGamepadEvent, gamepadEventInfo);
|
||||
NativeCFFI.lime_joystick_event_manager_register(handleJoystickEvent, joystickEventInfo);
|
||||
NativeCFFI.lime_key_event_manager_register(handleKeyEvent, keyEventInfo);
|
||||
NativeCFFI.lime_mouse_event_manager_register(handleMouseEvent, mouseEventInfo);
|
||||
NativeCFFI.lime_render_event_manager_register(handleRenderEvent, renderEventInfo);
|
||||
NativeCFFI.lime_text_event_manager_register(handleTextEvent, textEventInfo);
|
||||
NativeCFFI.lime_touch_event_manager_register(handleTouchEvent, touchEventInfo);
|
||||
NativeCFFI.lime_window_event_manager_register(handleWindowEvent, windowEventInfo);
|
||||
#if (ios || android || tvos)
|
||||
NativeCFFI.lime_sensor_event_manager_register(handleSensorEvent, sensorEventInfo);
|
||||
#end
|
||||
#end
|
||||
|
||||
#if (nodejs && lime_cffi)
|
||||
NativeCFFI.lime_application_init(handle);
|
||||
|
||||
var eventLoop = function()
|
||||
{
|
||||
var active = NativeCFFI.lime_application_update(handle);
|
||||
|
||||
if (!active)
|
||||
{
|
||||
untyped process.exitCode = NativeCFFI.lime_application_quit(handle);
|
||||
parent.onExit.dispatch(untyped process.exitCode);
|
||||
}
|
||||
else
|
||||
{
|
||||
untyped setImmediate(eventLoop);
|
||||
}
|
||||
}
|
||||
|
||||
untyped setImmediate(eventLoop);
|
||||
return 0;
|
||||
#elseif lime_cffi
|
||||
var result = NativeCFFI.lime_application_exec(handle);
|
||||
|
||||
#if (!webassembly && !ios && !nodejs)
|
||||
parent.onExit.dispatch(result);
|
||||
#end
|
||||
|
||||
return result;
|
||||
#end
|
||||
#end
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public function exit():Void
|
||||
{
|
||||
AudioManager.shutdown();
|
||||
|
||||
#if (!macro && lime_cffi)
|
||||
NativeCFFI.lime_application_quit(handle);
|
||||
#end
|
||||
}
|
||||
|
||||
private function handleApplicationEvent():Void
|
||||
{
|
||||
switch (applicationEventInfo.type)
|
||||
{
|
||||
case UPDATE:
|
||||
updateTimer();
|
||||
|
||||
parent.onUpdate.dispatch(applicationEventInfo.deltaTime);
|
||||
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
private function handleClipboardEvent():Void
|
||||
{
|
||||
Clipboard.__update();
|
||||
}
|
||||
|
||||
private function handleDropEvent():Void
|
||||
{
|
||||
for (window in parent.windows)
|
||||
{
|
||||
window.onDropFile.dispatch(#if hl @:privateAccess String.fromUTF8(dropEventInfo.file) #else dropEventInfo.file #end);
|
||||
}
|
||||
}
|
||||
|
||||
private function handleGamepadEvent():Void
|
||||
{
|
||||
switch (gamepadEventInfo.type)
|
||||
{
|
||||
case AXIS_MOVE:
|
||||
var gamepad = Gamepad.devices.get(gamepadEventInfo.id);
|
||||
if (gamepad != null) gamepad.onAxisMove.dispatch(gamepadEventInfo.axis, gamepadEventInfo.axisValue);
|
||||
|
||||
case BUTTON_DOWN:
|
||||
var gamepad = Gamepad.devices.get(gamepadEventInfo.id);
|
||||
if (gamepad != null) gamepad.onButtonDown.dispatch(gamepadEventInfo.button);
|
||||
|
||||
case BUTTON_UP:
|
||||
var gamepad = Gamepad.devices.get(gamepadEventInfo.id);
|
||||
if (gamepad != null) gamepad.onButtonUp.dispatch(gamepadEventInfo.button);
|
||||
|
||||
case CONNECT:
|
||||
Gamepad.__connect(gamepadEventInfo.id);
|
||||
|
||||
case DISCONNECT:
|
||||
Gamepad.__disconnect(gamepadEventInfo.id);
|
||||
}
|
||||
}
|
||||
|
||||
private function handleJoystickEvent():Void
|
||||
{
|
||||
switch (joystickEventInfo.type)
|
||||
{
|
||||
case AXIS_MOVE:
|
||||
var joystick = Joystick.devices.get(joystickEventInfo.id);
|
||||
if (joystick != null) joystick.onAxisMove.dispatch(joystickEventInfo.index, joystickEventInfo.x);
|
||||
|
||||
case HAT_MOVE:
|
||||
var joystick = Joystick.devices.get(joystickEventInfo.id);
|
||||
if (joystick != null) joystick.onHatMove.dispatch(joystickEventInfo.index, joystickEventInfo.eventValue);
|
||||
|
||||
case TRACKBALL_MOVE: // I guess this was just removed ??
|
||||
|
||||
case BUTTON_DOWN:
|
||||
var joystick = Joystick.devices.get(joystickEventInfo.id);
|
||||
if (joystick != null) joystick.onButtonDown.dispatch(joystickEventInfo.index);
|
||||
|
||||
case BUTTON_UP:
|
||||
var joystick = Joystick.devices.get(joystickEventInfo.id);
|
||||
if (joystick != null) joystick.onButtonUp.dispatch(joystickEventInfo.index);
|
||||
|
||||
case CONNECT:
|
||||
Joystick.__connect(joystickEventInfo.id);
|
||||
|
||||
case DISCONNECT:
|
||||
Joystick.__disconnect(joystickEventInfo.id);
|
||||
}
|
||||
}
|
||||
|
||||
private function handleKeyEvent():Void
|
||||
{
|
||||
var window = parent.__windowByID.get(keyEventInfo.windowID);
|
||||
|
||||
if (window != null)
|
||||
{
|
||||
var type:KeyEventType = keyEventInfo.type;
|
||||
var int32:Float = keyEventInfo.keyCode;
|
||||
var keyCode:KeyCode = Std.int(int32);
|
||||
var modifier:KeyModifier = keyEventInfo.modifier;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case KEY_DOWN:
|
||||
window.onKeyDown.dispatch(keyCode, modifier);
|
||||
|
||||
case KEY_UP:
|
||||
window.onKeyUp.dispatch(keyCode, modifier);
|
||||
}
|
||||
|
||||
#if (windows || linux)
|
||||
if (keyCode == RETURN)
|
||||
{
|
||||
if (type == KEY_DOWN)
|
||||
{
|
||||
if (toggleFullscreen && modifier.altKey && (!modifier.ctrlKey && !modifier.shiftKey && !modifier.metaKey))
|
||||
{
|
||||
toggleFullscreen = false;
|
||||
|
||||
if (!window.onKeyDown.canceled)
|
||||
{
|
||||
window.fullscreen = !window.fullscreen;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
toggleFullscreen = true;
|
||||
}
|
||||
}
|
||||
|
||||
#if rpi
|
||||
if (keyCode == ESCAPE && modifier == KeyModifier.NONE && type == KEY_UP && !window.onKeyUp.canceled)
|
||||
{
|
||||
System.exit(0);
|
||||
}
|
||||
#end
|
||||
#elseif mac
|
||||
if (keyCode == F)
|
||||
{
|
||||
if (type == KEY_DOWN)
|
||||
{
|
||||
if (toggleFullscreen && (modifier.ctrlKey && modifier.metaKey) && (!modifier.altKey && !modifier.shiftKey))
|
||||
{
|
||||
toggleFullscreen = false;
|
||||
|
||||
if (!window.onKeyDown.canceled)
|
||||
{
|
||||
window.fullscreen = !window.fullscreen;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
toggleFullscreen = true;
|
||||
}
|
||||
}
|
||||
#elseif android
|
||||
if (keyCode == APP_CONTROL_BACK && modifier == KeyModifier.NONE && type == KEY_UP && !window.onKeyUp.canceled)
|
||||
{
|
||||
var mainActivity = JNI.createStaticField("org/haxe/extension/Extension", "mainActivity", "Landroid/app/Activity;");
|
||||
var moveTaskToBack = JNI.createMemberMethod("android/app/Activity", "moveTaskToBack", "(Z)Z");
|
||||
|
||||
moveTaskToBack(mainActivity.get(), true);
|
||||
}
|
||||
#end
|
||||
}
|
||||
}
|
||||
|
||||
private function handleMouseEvent():Void
|
||||
{
|
||||
var window = parent.__windowByID.get(mouseEventInfo.windowID);
|
||||
|
||||
if (window != null)
|
||||
{
|
||||
switch (mouseEventInfo.type)
|
||||
{
|
||||
case MOUSE_DOWN:
|
||||
window.clickCount = mouseEventInfo.clickCount;
|
||||
window.onMouseDown.dispatch(mouseEventInfo.x, mouseEventInfo.y, mouseEventInfo.button);
|
||||
window.clickCount = 0;
|
||||
|
||||
case MOUSE_UP:
|
||||
window.clickCount = mouseEventInfo.clickCount;
|
||||
window.onMouseUp.dispatch(mouseEventInfo.x, mouseEventInfo.y, mouseEventInfo.button);
|
||||
window.clickCount = 0;
|
||||
|
||||
case MOUSE_MOVE:
|
||||
window.onMouseMove.dispatch(mouseEventInfo.x, mouseEventInfo.y);
|
||||
window.onMouseMoveRelative.dispatch(mouseEventInfo.movementX, mouseEventInfo.movementY);
|
||||
|
||||
case MOUSE_WHEEL:
|
||||
window.onMouseWheel.dispatch(mouseEventInfo.x, mouseEventInfo.y, UNKNOWN);
|
||||
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function handleRenderEvent():Void
|
||||
{
|
||||
// TODO: Allow windows to render independently
|
||||
|
||||
for (window in parent.__windows)
|
||||
{
|
||||
if (window == null) continue;
|
||||
|
||||
// parent.renderer = renderer;
|
||||
|
||||
switch (renderEventInfo.type)
|
||||
{
|
||||
case RENDER:
|
||||
if (window.context != null)
|
||||
{
|
||||
window.__backend.render();
|
||||
window.onRender.dispatch(window.context);
|
||||
|
||||
if (!window.onRender.canceled)
|
||||
{
|
||||
window.__backend.contextFlip();
|
||||
}
|
||||
}
|
||||
|
||||
case RENDER_CONTEXT_LOST:
|
||||
if (window.__backend.useHardware && window.context != null)
|
||||
{
|
||||
switch (window.context.type)
|
||||
{
|
||||
case OPENGL, OPENGLES, WEBGL:
|
||||
#if (lime_cffi && (lime_opengl || lime_opengles) && !display)
|
||||
var gl = window.context.gl;
|
||||
(gl : NativeOpenGLRenderContext).__contextLost();
|
||||
if (GL.context == gl) GL.context = null;
|
||||
#end
|
||||
|
||||
default:
|
||||
}
|
||||
|
||||
window.context = null;
|
||||
window.onRenderContextLost.dispatch();
|
||||
}
|
||||
|
||||
case RENDER_CONTEXT_RESTORED:
|
||||
if (window.__backend.useHardware)
|
||||
{
|
||||
// GL.context = new OpenGLRenderContext ();
|
||||
// window.context.gl = GL.context;
|
||||
|
||||
window.onRenderContextRestored.dispatch(window.context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function handleSensorEvent():Void
|
||||
{
|
||||
var sensor = Sensor.sensorByID.get(sensorEventInfo.id);
|
||||
|
||||
if (sensor != null)
|
||||
{
|
||||
sensor.onUpdate.dispatch(sensorEventInfo.x, sensorEventInfo.y, sensorEventInfo.z);
|
||||
}
|
||||
}
|
||||
|
||||
private function handleTextEvent():Void
|
||||
{
|
||||
var window = parent.__windowByID.get(textEventInfo.windowID);
|
||||
|
||||
if (window != null)
|
||||
{
|
||||
switch (textEventInfo.type)
|
||||
{
|
||||
case TEXT_INPUT:
|
||||
window.onTextInput.dispatch(#if hl @:privateAccess String.fromUTF8(textEventInfo.text) #else textEventInfo.text #end);
|
||||
|
||||
case TEXT_EDIT:
|
||||
window.onTextEdit.dispatch(#if hl @:privateAccess String.fromUTF8(textEventInfo.text) #else textEventInfo.text #end, textEventInfo.start,
|
||||
textEventInfo.length);
|
||||
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function handleTouchEvent():Void
|
||||
{
|
||||
switch (touchEventInfo.type)
|
||||
{
|
||||
case TOUCH_START:
|
||||
var touch = unusedTouchesPool.pop();
|
||||
|
||||
if (touch == null)
|
||||
{
|
||||
touch = new Touch(touchEventInfo.x, touchEventInfo.y, touchEventInfo.id, touchEventInfo.dx, touchEventInfo.dy, touchEventInfo.pressure,
|
||||
touchEventInfo.device);
|
||||
}
|
||||
else
|
||||
{
|
||||
touch.x = touchEventInfo.x;
|
||||
touch.y = touchEventInfo.y;
|
||||
touch.id = touchEventInfo.id;
|
||||
touch.dx = touchEventInfo.dx;
|
||||
touch.dy = touchEventInfo.dy;
|
||||
touch.pressure = touchEventInfo.pressure;
|
||||
touch.device = touchEventInfo.device;
|
||||
}
|
||||
|
||||
currentTouches.set(touch.id, touch);
|
||||
|
||||
Touch.onStart.dispatch(touch);
|
||||
|
||||
case TOUCH_END:
|
||||
var touch = currentTouches.get(touchEventInfo.id);
|
||||
|
||||
if (touch != null)
|
||||
{
|
||||
touch.x = touchEventInfo.x;
|
||||
touch.y = touchEventInfo.y;
|
||||
touch.dx = touchEventInfo.dx;
|
||||
touch.dy = touchEventInfo.dy;
|
||||
touch.pressure = touchEventInfo.pressure;
|
||||
|
||||
Touch.onEnd.dispatch(touch);
|
||||
|
||||
currentTouches.remove(touchEventInfo.id);
|
||||
unusedTouchesPool.add(touch);
|
||||
}
|
||||
|
||||
case TOUCH_MOVE:
|
||||
var touch = currentTouches.get(touchEventInfo.id);
|
||||
|
||||
if (touch != null)
|
||||
{
|
||||
touch.x = touchEventInfo.x;
|
||||
touch.y = touchEventInfo.y;
|
||||
touch.dx = touchEventInfo.dx;
|
||||
touch.dy = touchEventInfo.dy;
|
||||
touch.pressure = touchEventInfo.pressure;
|
||||
|
||||
Touch.onMove.dispatch(touch);
|
||||
}
|
||||
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
private function handleWindowEvent():Void
|
||||
{
|
||||
var window = parent.__windowByID.get(windowEventInfo.windowID);
|
||||
|
||||
if (window != null)
|
||||
{
|
||||
switch (windowEventInfo.type)
|
||||
{
|
||||
case WINDOW_ACTIVATE:
|
||||
advanceTimer();
|
||||
window.onActivate.dispatch();
|
||||
AudioManager.resume();
|
||||
|
||||
case WINDOW_CLOSE:
|
||||
window.close();
|
||||
|
||||
case WINDOW_DEACTIVATE:
|
||||
window.onDeactivate.dispatch();
|
||||
AudioManager.suspend();
|
||||
pauseTimer = System.getTimer();
|
||||
|
||||
case WINDOW_ENTER:
|
||||
window.onEnter.dispatch();
|
||||
|
||||
case WINDOW_EXPOSE:
|
||||
window.onExpose.dispatch();
|
||||
|
||||
case WINDOW_FOCUS_IN:
|
||||
window.onFocusIn.dispatch();
|
||||
|
||||
case WINDOW_FOCUS_OUT:
|
||||
window.onFocusOut.dispatch();
|
||||
|
||||
case WINDOW_LEAVE:
|
||||
window.onLeave.dispatch();
|
||||
|
||||
case WINDOW_MAXIMIZE:
|
||||
window.__maximized = true;
|
||||
window.__fullscreen = false;
|
||||
window.__minimized = false;
|
||||
window.onMaximize.dispatch();
|
||||
|
||||
case WINDOW_MINIMIZE:
|
||||
window.__minimized = true;
|
||||
window.__maximized = false;
|
||||
window.__fullscreen = false;
|
||||
window.onMinimize.dispatch();
|
||||
|
||||
case WINDOW_MOVE:
|
||||
window.__x = windowEventInfo.x;
|
||||
window.__y = windowEventInfo.y;
|
||||
window.onMove.dispatch(windowEventInfo.x, windowEventInfo.y);
|
||||
|
||||
case WINDOW_RESIZE:
|
||||
window.__width = windowEventInfo.width;
|
||||
window.__height = windowEventInfo.height;
|
||||
window.onResize.dispatch(windowEventInfo.width, windowEventInfo.height);
|
||||
|
||||
case WINDOW_RESTORE:
|
||||
window.__fullscreen = false;
|
||||
window.__minimized = false;
|
||||
window.onRestore.dispatch();
|
||||
|
||||
case WINDOW_SHOW:
|
||||
window.onShow.dispatch();
|
||||
|
||||
case WINDOW_HIDE:
|
||||
window.onHide.dispatch();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function updateTimer():Void
|
||||
{
|
||||
#if lime_cffi
|
||||
if (Timer.sRunningTimers.length > 0)
|
||||
{
|
||||
var currentTime = System.getTimer();
|
||||
var foundNull = false;
|
||||
var timer;
|
||||
|
||||
for (i in 0...Timer.sRunningTimers.length)
|
||||
{
|
||||
timer = Timer.sRunningTimers[i];
|
||||
|
||||
if (timer != null && timer.mRunning)
|
||||
{
|
||||
if (currentTime >= timer.mFireAt)
|
||||
{
|
||||
timer.mFireAt += timer.mTime;
|
||||
timer.run();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foundNull = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (foundNull)
|
||||
{
|
||||
Timer.sRunningTimers = Timer.sRunningTimers.filter(function(val)
|
||||
{
|
||||
return val != null && val.mRunning;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#if (haxe_ver >= 4.2)
|
||||
#if target.threaded
|
||||
sys.thread.Thread.current().events.progress();
|
||||
#else
|
||||
// Duplicate code required because Haxe 3 can't handle
|
||||
// #if (haxe_ver >= 4.2 && target.threaded)
|
||||
@:privateAccess haxe.EntryPoint.processEvents();
|
||||
#end
|
||||
#else
|
||||
@:privateAccess haxe.EntryPoint.processEvents();
|
||||
#end
|
||||
#end
|
||||
}
|
||||
}
|
||||
|
||||
@:keep /*private*/ class ApplicationEventInfo
|
||||
{
|
||||
public var deltaTime:Int;
|
||||
public var type:ApplicationEventType;
|
||||
|
||||
public function new(type:ApplicationEventType = null, deltaTime:Int = 0)
|
||||
{
|
||||
this.type = type;
|
||||
this.deltaTime = deltaTime;
|
||||
}
|
||||
|
||||
public function clone():ApplicationEventInfo
|
||||
{
|
||||
return new ApplicationEventInfo(type, deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract ApplicationEventType(Int)
|
||||
{
|
||||
var UPDATE = 0;
|
||||
var EXIT = 1;
|
||||
}
|
||||
|
||||
@:keep /*private*/ class ClipboardEventInfo
|
||||
{
|
||||
public var type:ClipboardEventType;
|
||||
|
||||
public function new(type:ClipboardEventType = null)
|
||||
{
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public function clone():ClipboardEventInfo
|
||||
{
|
||||
return new ClipboardEventInfo(type);
|
||||
}
|
||||
}
|
||||
|
||||
#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract ClipboardEventType(Int)
|
||||
{
|
||||
var UPDATE = 0;
|
||||
}
|
||||
|
||||
@:keep /*private*/ class DropEventInfo
|
||||
{
|
||||
public var file:#if hl hl.Bytes #else String #end;
|
||||
public var type:DropEventType;
|
||||
|
||||
public function new(type:DropEventType = null, file = null)
|
||||
{
|
||||
this.type = type;
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
public function clone():DropEventInfo
|
||||
{
|
||||
return new DropEventInfo(type, file);
|
||||
}
|
||||
}
|
||||
|
||||
#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract DropEventType(Int)
|
||||
{
|
||||
var DROP_FILE = 0;
|
||||
}
|
||||
|
||||
@:keep /*private*/ class GamepadEventInfo
|
||||
{
|
||||
public var axis:Int;
|
||||
public var button:Int;
|
||||
public var id:Int;
|
||||
public var type:GamepadEventType;
|
||||
public var axisValue:Float;
|
||||
|
||||
public function new(type:GamepadEventType = null, id:Int = 0, button:Int = 0, axis:Int = 0, value:Float = 0)
|
||||
{
|
||||
this.type = type;
|
||||
this.id = id;
|
||||
this.button = button;
|
||||
this.axis = axis;
|
||||
this.axisValue = value;
|
||||
}
|
||||
|
||||
public function clone():GamepadEventInfo
|
||||
{
|
||||
return new GamepadEventInfo(type, id, button, axis, axisValue);
|
||||
}
|
||||
}
|
||||
|
||||
#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract GamepadEventType(Int)
|
||||
{
|
||||
var AXIS_MOVE = 0;
|
||||
var BUTTON_DOWN = 1;
|
||||
var BUTTON_UP = 2;
|
||||
var CONNECT = 3;
|
||||
var DISCONNECT = 4;
|
||||
}
|
||||
|
||||
@:keep /*private*/ class JoystickEventInfo
|
||||
{
|
||||
public var id:Int;
|
||||
public var index:Int;
|
||||
public var type:JoystickEventType;
|
||||
public var eventValue:Int;
|
||||
public var x:Float;
|
||||
public var y:Float;
|
||||
|
||||
public function new(type:JoystickEventType = null, id:Int = 0, index:Int = 0, value:Int = 0, x:Float = 0, y:Float = 0)
|
||||
{
|
||||
this.type = type;
|
||||
this.id = id;
|
||||
this.index = index;
|
||||
this.eventValue = value;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
public function clone():JoystickEventInfo
|
||||
{
|
||||
return new JoystickEventInfo(type, id, index, eventValue, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract JoystickEventType(Int)
|
||||
{
|
||||
var AXIS_MOVE = 0;
|
||||
var HAT_MOVE = 1;
|
||||
var TRACKBALL_MOVE = 2;
|
||||
var BUTTON_DOWN = 3;
|
||||
var BUTTON_UP = 4;
|
||||
var CONNECT = 5;
|
||||
var DISCONNECT = 6;
|
||||
}
|
||||
|
||||
@:keep /*private*/ class KeyEventInfo
|
||||
{
|
||||
public var keyCode: Float;
|
||||
public var modifier:Int;
|
||||
public var type:KeyEventType;
|
||||
public var windowID:Int;
|
||||
|
||||
public function new(type:KeyEventType = null, windowID:Int = 0, keyCode: Float = 0, modifier:Int = 0)
|
||||
{
|
||||
this.type = type;
|
||||
this.windowID = windowID;
|
||||
this.keyCode = keyCode;
|
||||
this.modifier = modifier;
|
||||
}
|
||||
|
||||
public function clone():KeyEventInfo
|
||||
{
|
||||
return new KeyEventInfo(type, windowID, keyCode, modifier);
|
||||
}
|
||||
}
|
||||
|
||||
#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract KeyEventType(Int)
|
||||
{
|
||||
var KEY_DOWN = 0;
|
||||
var KEY_UP = 1;
|
||||
}
|
||||
|
||||
@:keep /*private*/ class MouseEventInfo
|
||||
{
|
||||
public var button:Int;
|
||||
public var movementX:Float;
|
||||
public var movementY:Float;
|
||||
public var type:MouseEventType;
|
||||
public var windowID:Int;
|
||||
public var x:Float;
|
||||
public var y:Float;
|
||||
public var clickCount:Int;
|
||||
|
||||
public function new(type:MouseEventType = null, windowID:Int = 0, x:Float = 0, y:Float = 0, button:Int = 0, movementX:Float = 0, movementY:Float = 0, clickCount:Int = 0)
|
||||
{
|
||||
this.type = type;
|
||||
this.windowID = 0;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.button = button;
|
||||
this.movementX = movementX;
|
||||
this.movementY = movementY;
|
||||
this.clickCount = clickCount;
|
||||
}
|
||||
|
||||
public function clone():MouseEventInfo
|
||||
{
|
||||
return new MouseEventInfo(type, windowID, x, y, button, movementX, movementY, clickCount);
|
||||
}
|
||||
}
|
||||
|
||||
#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract MouseEventType(Int)
|
||||
{
|
||||
var MOUSE_DOWN = 0;
|
||||
var MOUSE_UP = 1;
|
||||
var MOUSE_MOVE = 2;
|
||||
var MOUSE_WHEEL = 3;
|
||||
}
|
||||
|
||||
@:keep /*private*/ class RenderEventInfo
|
||||
{
|
||||
public var type:RenderEventType;
|
||||
|
||||
public function new(type:RenderEventType = null)
|
||||
{
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public function clone():RenderEventInfo
|
||||
{
|
||||
return new RenderEventInfo(type);
|
||||
}
|
||||
}
|
||||
|
||||
#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract RenderEventType(Int)
|
||||
{
|
||||
var RENDER = 0;
|
||||
var RENDER_CONTEXT_LOST = 1;
|
||||
var RENDER_CONTEXT_RESTORED = 2;
|
||||
}
|
||||
|
||||
@:keep /*private*/ class SensorEventInfo
|
||||
{
|
||||
public var id:Int;
|
||||
public var x:Float;
|
||||
public var y:Float;
|
||||
public var z:Float;
|
||||
public var type:SensorEventType;
|
||||
|
||||
public function new(type:SensorEventType = null, id:Int = 0, x:Float = 0, y:Float = 0, z:Float = 0)
|
||||
{
|
||||
this.type = type;
|
||||
this.id = id;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
public function clone():SensorEventInfo
|
||||
{
|
||||
return new SensorEventInfo(type, id, x, y, z);
|
||||
}
|
||||
}
|
||||
|
||||
#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract SensorEventType(Int)
|
||||
{
|
||||
var ACCELEROMETER = 0;
|
||||
}
|
||||
|
||||
@:keep /*private*/ class TextEventInfo
|
||||
{
|
||||
public var id:Int;
|
||||
public var length:Int;
|
||||
public var start:Int;
|
||||
public var text:#if hl hl.Bytes #else String #end;
|
||||
public var type:TextEventType;
|
||||
public var windowID:Int;
|
||||
|
||||
public function new(type:TextEventType = null, windowID:Int = 0, text = null, start:Int = 0, length:Int = 0)
|
||||
{
|
||||
this.type = type;
|
||||
this.windowID = windowID;
|
||||
this.text = text;
|
||||
this.start = start;
|
||||
this.length = length;
|
||||
}
|
||||
|
||||
public function clone():TextEventInfo
|
||||
{
|
||||
return new TextEventInfo(type, windowID, text, start, length);
|
||||
}
|
||||
}
|
||||
|
||||
#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract TextEventType(Int)
|
||||
{
|
||||
var TEXT_INPUT = 0;
|
||||
var TEXT_EDIT = 1;
|
||||
}
|
||||
|
||||
@:keep /*private*/ class TouchEventInfo
|
||||
{
|
||||
public var device:Int;
|
||||
public var dx:Float;
|
||||
public var dy:Float;
|
||||
public var id:Int;
|
||||
public var pressure:Float;
|
||||
public var type:TouchEventType;
|
||||
public var x:Float;
|
||||
public var y:Float;
|
||||
|
||||
public function new(type:TouchEventType = null, x:Float = 0, y:Float = 0, id:Int = 0, dx:Float = 0, dy:Float = 0, pressure:Float = 0, device:Int = 0)
|
||||
{
|
||||
this.type = type;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.id = id;
|
||||
this.dx = dx;
|
||||
this.dy = dy;
|
||||
this.pressure = pressure;
|
||||
this.device = device;
|
||||
}
|
||||
|
||||
public function clone():TouchEventInfo
|
||||
{
|
||||
return new TouchEventInfo(type, x, y, id, dx, dy, pressure, device);
|
||||
}
|
||||
}
|
||||
|
||||
#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract TouchEventType(Int)
|
||||
{
|
||||
var TOUCH_START = 0;
|
||||
var TOUCH_END = 1;
|
||||
var TOUCH_MOVE = 2;
|
||||
}
|
||||
|
||||
@:keep /*private*/ class WindowEventInfo
|
||||
{
|
||||
public var height:Int;
|
||||
public var type:WindowEventType;
|
||||
public var width:Int;
|
||||
public var windowID:Int;
|
||||
public var x:Int;
|
||||
public var y:Int;
|
||||
|
||||
public function new(type:WindowEventType = null, windowID:Int = 0, width:Int = 0, height:Int = 0, x:Int = 0, y:Int = 0)
|
||||
{
|
||||
this.type = type;
|
||||
this.windowID = windowID;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
public function clone():WindowEventInfo
|
||||
{
|
||||
return new WindowEventInfo(type, windowID, width, height, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract WindowEventType(Int)
|
||||
{
|
||||
var WINDOW_ACTIVATE = 0;
|
||||
var WINDOW_CLOSE = 1;
|
||||
var WINDOW_DEACTIVATE = 2;
|
||||
var WINDOW_ENTER = 3;
|
||||
var WINDOW_EXPOSE = 4;
|
||||
var WINDOW_FOCUS_IN = 5;
|
||||
var WINDOW_FOCUS_OUT = 6;
|
||||
var WINDOW_LEAVE = 7;
|
||||
var WINDOW_MAXIMIZE = 8;
|
||||
var WINDOW_MINIMIZE = 9;
|
||||
var WINDOW_MOVE = 10;
|
||||
var WINDOW_RESIZE = 11;
|
||||
var WINDOW_RESTORE = 12;
|
||||
var WINDOW_SHOW = 13;
|
||||
var WINDOW_HIDE = 14;
|
||||
}
|
||||
@@ -1,804 +0,0 @@
|
||||
package lime._internal.backend.native;
|
||||
|
||||
import sys.thread.Thread;
|
||||
import sys.thread.Mutex;
|
||||
|
||||
import haxe.Timer;
|
||||
import haxe.Int64;
|
||||
|
||||
import lime.media.openal.AL;
|
||||
import lime.media.openal.ALBuffer;
|
||||
import lime.media.openal.ALSource;
|
||||
|
||||
#if lime_vorbis
|
||||
import lime.media.vorbis.Vorbis;
|
||||
import lime.media.vorbis.VorbisFile;
|
||||
import lime.media.vorbis.VorbisInfo;
|
||||
#end
|
||||
|
||||
import lime.math.Vector2;
|
||||
import lime.math.Vector4;
|
||||
import lime.media.AudioBuffer;
|
||||
import lime.media.AudioSource;
|
||||
import lime.system.Endian;
|
||||
import lime.system.System;
|
||||
import lime.utils.ArrayBuffer;
|
||||
import lime.utils.ArrayBufferView.TypedArrayType;
|
||||
import lime.utils.ArrayBufferView;
|
||||
|
||||
#if !lime_debug
|
||||
@:fileXml('tags="haxe,release"')
|
||||
@:noDebug
|
||||
#end
|
||||
@:access(haxe.Timer)
|
||||
@:access(lime.media.AudioBuffer)
|
||||
@:access(lime.utils.ArrayBufferView)
|
||||
class NativeAudioSource {
|
||||
// Can hold up to 3 hours 44100 sampleRate audio, if you are into that, theorically.
|
||||
|
||||
public static var STREAM_BUFFER_SAMPLES:Int = 0x2000; // how much buffers will be generating every frequency (doesnt have to be pow of 2?).
|
||||
public static var STREAM_MIN_BUFFERS:Int = 2; // how much buffers can a stream hold on minimum or starting.
|
||||
public static var STREAM_MAX_BUFFERS:Int = 8; // how much limit of a buffers can be used for streamed audios, must be higher than minimum.
|
||||
public static var STREAM_MAX_FLUSH_BUFFERS:Int = 3; // how much buffers can it play.
|
||||
public static var STREAM_PROCESS_BUFFERS:Int = 2; // how much buffers can be processed in a frequency tick.
|
||||
public static var POOL_MAX_BUFFERS:Int = 32; // how much buffers for the pool to hold.
|
||||
|
||||
public static var moreFormatsSupported:Null<Bool>;
|
||||
public static var loopPointsSupported:Null<Bool>;
|
||||
public static var stereoAnglesExtensionSupported:Null<Bool>;
|
||||
public static var latencyExtensionSupported:Null<Bool>;
|
||||
|
||||
private static var bufferDataPool:Array<ArrayBufferView> = [];
|
||||
private static var isBigEndian:Bool = System.endianness == Endian.BIG_ENDIAN;
|
||||
|
||||
public static function getALFormat(bitsPerSample:Int, channels:Int):Int {
|
||||
if (moreFormatsSupported == null) moreFormatsSupported = AL.isExtensionPresent("AL_EXT_MCFORMATS");
|
||||
|
||||
// There was a code to also supports for X-Fi Renderer but, that kind of device is
|
||||
// rare nowadays and none of sounds should have more than 24 bitsPerSample.
|
||||
// https://github.com/kcat/openal-soft/issues/934
|
||||
|
||||
if (channels > 2 && moreFormatsSupported) {
|
||||
if (channels == 3) return bitsPerSample == 32 ? 0x1209 : (bitsPerSample == 16 ? 0x1208 : 0x1207);
|
||||
else if (channels == 4) return bitsPerSample == 32 ? 0x1206 : (bitsPerSample == 16 ? 0x1205 : 0x1204);
|
||||
else if (channels == 6) return bitsPerSample == 32 ? 0x120C : (bitsPerSample == 16 ? 0x120B : 0x120A);
|
||||
else if (channels == 7) return bitsPerSample == 32 ? 0x120F : (bitsPerSample == 16 ? 0x120E : 0x120D);
|
||||
else if (channels == 8) return bitsPerSample == 32 ? 0x1212 : (bitsPerSample == 16 ? 0x1211 : 0x1210);
|
||||
else return AL.FORMAT_MONO8;
|
||||
}
|
||||
else if (bitsPerSample == 32 && moreFormatsSupported) return channels == 2 ? 0x1203 : 0x1202;
|
||||
else if (channels == 2) return bitsPerSample == 16 ? AL.FORMAT_STEREO16 : AL.FORMAT_STEREO8;
|
||||
else return bitsPerSample == 16 ? AL.FORMAT_MONO16 : AL.FORMAT_MONO8;
|
||||
}
|
||||
|
||||
private static function resetTimer(timer:Timer, time:Float, callback:Void->Void):Timer {
|
||||
if (timer == null) (timer = new Timer(time)).run = callback;
|
||||
else {
|
||||
timer.mTime = time;
|
||||
timer.mFireAt = Timer.getMS() + time;
|
||||
timer.mRunning = true;
|
||||
timer.run = callback;
|
||||
|
||||
if (!Timer.sRunningTimers.contains(timer)) Timer.sRunningTimers.push(timer);
|
||||
}
|
||||
return timer;
|
||||
}
|
||||
|
||||
inline private static function getFloat(x:Int64):Float return x.high * 4294967296. + (x.low >> 0);
|
||||
|
||||
// Backward Compatibility Variables
|
||||
var handle(get, set):ALSource; inline function get_handle() return source; inline function set_handle(v) return source = v;
|
||||
var timer(get, set):Timer; inline function get_timer() return completeTimer; inline function set_timer(v) return completeTimer = v;
|
||||
var length(get, set):Null<Float>; inline function get_length() return endTime; inline function set_length(v) return endTime = v;
|
||||
var toLoop(get, set):Int; inline function get_toLoop() return streamLoops; inline function set_toLoop(v) return streamLoops = v;
|
||||
var bufferSizes(get, set):Array<Int>; inline function get_bufferSizes() return bufferLengths; inline function set_bufferSizes(v) return bufferLengths = v;
|
||||
|
||||
var parent:AudioSource;
|
||||
var disposed:Bool;
|
||||
var streamed:Bool;
|
||||
var playing:Bool;
|
||||
var completed:Bool;
|
||||
var lastTime:Float;
|
||||
|
||||
var position:Vector4;
|
||||
var angles:Vector2;
|
||||
var anglesArray:Array<Float>;
|
||||
var endTime:Null<Float>;
|
||||
var loopTime:Float;
|
||||
var loops:Int;
|
||||
|
||||
var channels:Int;
|
||||
var sampleRate:Int;
|
||||
var wordSize:Int; // is bitsPerSample >> 3, ex: 8 bits is 1, 16 bits is 2, 32 bits is 4, etc.
|
||||
var samples:Int;
|
||||
var dataLength:Int;
|
||||
var duration:Float;
|
||||
|
||||
var completeTimer:Timer;
|
||||
var source:ALSource;
|
||||
var buffer:ALBuffer;
|
||||
var standaloneBuffer:Bool;
|
||||
var format:Int; // AL.FORMAT_...
|
||||
var arrayType:TypedArrayType;
|
||||
var loopPoints:Array<Int>; // In Samples
|
||||
|
||||
static var streamSources:Array<NativeAudioSource> = [];
|
||||
static var queuedStreamSources:Array<NativeAudioSource> = [];
|
||||
|
||||
static var streamMutex:Mutex = new Mutex();
|
||||
static var streamTimer:Timer;
|
||||
|
||||
#if !ALLOW_MULTITHREADING
|
||||
static var wasEmpty:Bool = false;
|
||||
static var threadRunning:Bool = false;
|
||||
static var streamThread:Thread;
|
||||
#end
|
||||
|
||||
var streamRemove:Bool;
|
||||
|
||||
var bufferLength:Int; // Size in bytes for current streamed audio buffers.
|
||||
var requestBuffers:Int;
|
||||
var queuedBuffers:Int;
|
||||
var streamLoops:Int;
|
||||
var streamEnded:Bool;
|
||||
|
||||
// ORDERING IS CURRENT TO NEXT, STARTS FROM THE LENGTH OF THE ARRAYS
|
||||
var bufferDatas:Array<ArrayBufferView>;
|
||||
var bufferTimes:Array<Float>;
|
||||
var bufferLengths:Array<Int>;
|
||||
|
||||
var buffers:Array<ALBuffer>;
|
||||
var nextBuffer:Int = 0;
|
||||
|
||||
public function new(parent:AudioSource) {
|
||||
this.parent = parent;
|
||||
|
||||
if (loopPointsSupported == null) loopPointsSupported = AL.isExtensionPresent("AL_SOFT_loop_points");
|
||||
if (stereoAnglesExtensionSupported == null) stereoAnglesExtensionSupported = AL.isExtensionPresent("AL_EXT_STEREO_ANGLES");
|
||||
if (latencyExtensionSupported == null) latencyExtensionSupported = AL.isExtensionPresent("AL_SOFT_source_latency");
|
||||
}
|
||||
|
||||
public function dispose() {
|
||||
streamMutex.acquire();
|
||||
removeStream();
|
||||
|
||||
stop();
|
||||
disposed = true;
|
||||
|
||||
position = null;
|
||||
angles = null;
|
||||
anglesArray = null;
|
||||
|
||||
if (source != null) {
|
||||
if (streamed) AL.sourceUnqueueBuffers(source, AL.getSourcei(source, AL.BUFFERS_QUEUED));
|
||||
else AL.sourcei(source, AL.BUFFER, AL.NONE);
|
||||
|
||||
AL.deleteSource(source);
|
||||
source = null;
|
||||
}
|
||||
|
||||
if (standaloneBuffer && buffer != null) {
|
||||
AL.bufferData(buffer, 0, null, 0, 0);
|
||||
AL.deleteBuffer(buffer);
|
||||
buffer = null;
|
||||
}
|
||||
loopPoints = null;
|
||||
|
||||
if (buffers != null) {
|
||||
for (buffer in buffers) AL.bufferData(buffer, 0, null, 0, 0);
|
||||
AL.deleteBuffers(buffers);
|
||||
buffers = null;
|
||||
}
|
||||
|
||||
if (bufferDatas != null) {
|
||||
for (data in bufferDatas) if (bufferDataPool.length < POOL_MAX_BUFFERS) bufferDataPool.push(data);
|
||||
bufferDatas = null;
|
||||
}
|
||||
|
||||
completeTimer = null;
|
||||
|
||||
bufferTimes = null;
|
||||
bufferLengths = null;
|
||||
|
||||
streamMutex.release();
|
||||
}
|
||||
|
||||
public function init() {
|
||||
if (source != null || (disposed = parent == null || (source = AL.createSource()) == null)) return;
|
||||
AL.sourcef(source, AL.MAX_GAIN, 32);
|
||||
AL.distanceModel(AL.NONE);
|
||||
|
||||
if (position == null) position = new Vector4();
|
||||
if (angles == null) angles = new Vector2(Math.PI / 6, -Math.PI / 6); // https://github.com/kcat/openal-soft/issues/1032
|
||||
if (loopPoints == null) loopPoints = [0, 0];
|
||||
if (stereoAnglesExtensionSupported && anglesArray == null) anglesArray = [0, 0];
|
||||
|
||||
resetBuffer();
|
||||
}
|
||||
|
||||
public function resetBuffer() {
|
||||
if (parent.buffer == null) return;
|
||||
|
||||
streamMutex.acquire();
|
||||
removeStream();
|
||||
|
||||
stop();
|
||||
|
||||
if (streamed) AL.sourceUnqueueBuffers(source, AL.getSourcei(source, AL.BUFFERS_QUEUED));
|
||||
else AL.sourcei(source, AL.BUFFER, AL.NONE);
|
||||
|
||||
streamMutex.release();
|
||||
|
||||
final audioBuffer = parent.buffer;
|
||||
channels = audioBuffer.channels;
|
||||
sampleRate = audioBuffer.sampleRate;
|
||||
wordSize = audioBuffer.bitsPerSample >> 3;
|
||||
format = getALFormat(audioBuffer.bitsPerSample, channels);
|
||||
arrayType = wordSize == 4 ? TypedArrayType.Uint32 : (wordSize == 2 ? TypedArrayType.Uint16 : TypedArrayType.Int8);
|
||||
standaloneBuffer = false;
|
||||
loopTime = 0;
|
||||
endTime = null;
|
||||
|
||||
if (buffer != null) {
|
||||
if (standaloneBuffer) {
|
||||
AL.bufferData(buffer, 0, null, 0, 0);
|
||||
AL.deleteBuffer(buffer);
|
||||
}
|
||||
buffer = null;
|
||||
}
|
||||
|
||||
if (audioBuffer.data != null) {
|
||||
streamed = false;
|
||||
samples = Std.int((dataLength = audioBuffer.data.byteLength) / wordSize / channels);
|
||||
}
|
||||
#if lime_vorbis
|
||||
else if (audioBuffer.__srcVorbisFile != null) {
|
||||
streamed = true;
|
||||
dataLength = Std.int(getFloat(samples = Int64.toInt(audioBuffer.__srcVorbisFile.pcmTotal())) * channels * wordSize);
|
||||
}
|
||||
#end
|
||||
else return;
|
||||
|
||||
duration = getFloat(samples) / sampleRate * 1000;
|
||||
|
||||
loopPoints[0] = 0;
|
||||
loopPoints[1] = samples - 1;
|
||||
|
||||
if (streamed) {
|
||||
final length = STREAM_BUFFER_SAMPLES * channels;
|
||||
bufferLength = length * wordSize;
|
||||
|
||||
if (buffers == null) buffers = AL.genBuffers(STREAM_MAX_FLUSH_BUFFERS);
|
||||
if (bufferDatas == null) {
|
||||
bufferDatas = [];
|
||||
bufferTimes = [];
|
||||
bufferLengths = [];
|
||||
}
|
||||
|
||||
for (i in 0...STREAM_MAX_BUFFERS) {
|
||||
bufferTimes[i] = 0.0;
|
||||
bufferLengths[i] = 0;
|
||||
|
||||
var data = bufferDataPool.pop();
|
||||
if (data == null) data = new ArrayBufferView(length, arrayType);
|
||||
else {
|
||||
data.type = arrayType;
|
||||
data.bytesPerElement = wordSize;
|
||||
data.length = length;
|
||||
if (data.byteLength != bufferLength) {
|
||||
#if cpp
|
||||
data.buffer.getData().resize(bufferLength);
|
||||
data.buffer.fill(data.byteLength, bufferLength - data.byteLength, 0);
|
||||
@:privateAccess data.buffer.length = bufferLength;
|
||||
#else
|
||||
data.buffer = new ArrayBuffer(bufferLength);
|
||||
#end
|
||||
}
|
||||
data.byteLength = bufferLength;
|
||||
}
|
||||
bufferDatas[i] = data;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (buffers != null) {
|
||||
for (buffer in buffers) AL.bufferData(buffer, 0, null, 0, 0);
|
||||
AL.deleteBuffers(buffers);
|
||||
buffers = null;
|
||||
|
||||
for (data in bufferDatas) if (bufferDataPool.length < POOL_MAX_BUFFERS) bufferDataPool.push(data);
|
||||
bufferDatas.resize(0);
|
||||
}
|
||||
|
||||
if (audioBuffer.__srcBuffer != null) {
|
||||
if (AL.getBufferi(audioBuffer.__srcBuffer, AL.SIZE) != dataLength) {
|
||||
AL.bufferData(audioBuffer.__srcBuffer, 0, null, 0, 0);
|
||||
AL.deleteBuffer(audioBuffer.__srcBuffer);
|
||||
if ((buffer = audioBuffer.__srcBuffer = AL.createBuffer()) != null)
|
||||
AL.bufferData(buffer, format, audioBuffer.data, dataLength, sampleRate);
|
||||
}
|
||||
else
|
||||
buffer = audioBuffer.__srcBuffer;
|
||||
}
|
||||
else if ((buffer = audioBuffer.__srcBuffer = AL.createBuffer()) != null)
|
||||
AL.bufferData(buffer, format, audioBuffer.data, dataLength, sampleRate);
|
||||
|
||||
AL.sourcei(source, AL.BUFFER, buffer);
|
||||
}
|
||||
|
||||
updateLoopPoints();
|
||||
}
|
||||
|
||||
function updateLoopPoints() {
|
||||
if (loops <= 0) return AL.sourcei(source, AL.LOOPING, AL.FALSE);
|
||||
|
||||
var time = getCurrentTime() + parent.offset, length = getRealLength();
|
||||
final fixed = time >= length;
|
||||
if (fixed) time = loopTime;
|
||||
|
||||
if (!streamed) {
|
||||
var internalLoop = AL.TRUE;
|
||||
if (length < duration - 1 && loopTime > 1) {
|
||||
if (!loopPointsSupported) internalLoop = AL.FALSE;
|
||||
else {
|
||||
AL.sourceStop(source);
|
||||
AL.sourcei(source, AL.BUFFER, AL.NONE);
|
||||
if (!standaloneBuffer) {
|
||||
if (standaloneBuffer = (buffer = AL.createBuffer()) != null)
|
||||
AL.bufferData(buffer, format, parent.buffer.data, dataLength, sampleRate);
|
||||
else {
|
||||
buffer = parent.buffer.__srcBuffer;
|
||||
internalLoop = AL.FALSE;
|
||||
}
|
||||
}
|
||||
if (internalLoop == AL.TRUE) AL.bufferiv(buffer, 0x2015/*AL.LOOP_POINTS_SOFT*/, loopPoints);
|
||||
AL.sourcei(source, AL.BUFFER, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
AL.sourcei(source, AL.LOOPING, internalLoop);
|
||||
if (playing) setCurrentTime(time - parent.offset);
|
||||
else updateCompleteTimer();
|
||||
}
|
||||
else if (playing && (fixed || streamLoops > 0)) {
|
||||
AL.sourcei(source, AL.LOOPING, AL.FALSE);
|
||||
AL.sourceStop(source);
|
||||
snapBuffersToTime(time, streamLoops > 0);
|
||||
AL.sourcePlay(source);
|
||||
}
|
||||
}
|
||||
|
||||
// https://github.com/xiph/vorbis/blob/master/CHANGES#L39 bug in libvorbis <= 1.3.4
|
||||
inline function streamSeek(samples:Int64)
|
||||
if (samples <= 1) parent.buffer.__srcVorbisFile.rawSeek(0); else parent.buffer.__srcVorbisFile.pcmSeek(samples);
|
||||
|
||||
inline function streamTell():Int64
|
||||
return parent.buffer.__srcVorbisFile.pcmTell();
|
||||
|
||||
inline function streamRead(buffer:ArrayBuffer, position:Int, length:Int, wordSize:Int):Int
|
||||
return parent.buffer.__srcVorbisFile.read(buffer, position, length, isBigEndian, wordSize, true);
|
||||
|
||||
function readToBufferData(data:ArrayBufferView, currentPCM:Int64):Int {
|
||||
var length = (Int64.ofInt(loopPoints[1]) - currentPCM) * channels * wordSize;
|
||||
var n = length < bufferLength ? length.low : bufferLength, total = 0, result = 0, wasEOF = false;
|
||||
|
||||
try while (total < bufferLength) {
|
||||
result = n > 0 ? streamRead(data.buffer, total, n, wordSize) : 0;
|
||||
|
||||
if (result == Vorbis.HOLE) continue;
|
||||
else if (result <= Vorbis.EREAD) break;
|
||||
else if (result == 0) {
|
||||
if (streamEnded = wasEOF == (wasEOF = true) || loops <= streamLoops) break;
|
||||
|
||||
streamSeek(Int64.ofInt(loopPoints[0]));
|
||||
streamLoops++;
|
||||
if ((length = Int64.ofInt(loopPoints[1] - loopPoints[0]) * channels * wordSize) < (n = bufferLength - total)) n = length.low;
|
||||
}
|
||||
else {
|
||||
total += result;
|
||||
n -= result;
|
||||
wasEOF = false;
|
||||
}
|
||||
}
|
||||
catch (e:haxe.Exception) {
|
||||
trace('NativeAudioSource readToBufferData Bug! error: ${e.details()}, streamEnded: $streamEnded, total: $total, n: $n');
|
||||
return result;
|
||||
}
|
||||
|
||||
if (result < 0) {
|
||||
trace('NativeAudioSource readToBufferData Bug! reading result is $result, streamEnded: $streamEnded, total: $total, n: $n');
|
||||
return result;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
function fillBuffers(n:Int) {
|
||||
final max = STREAM_MAX_BUFFERS - 1;
|
||||
var i:Int, j:Int, data:ArrayBufferView, pcm:Int64, decoded:Int;
|
||||
while (n-- > 0 && !streamEnded && (decoded = readToBufferData(data = bufferDatas[i = max - requestBuffers], pcm = streamTell())) > 0) {
|
||||
j = i;
|
||||
while (i < max) {
|
||||
bufferDatas[i] = bufferDatas[++j];
|
||||
bufferTimes[i] = bufferTimes[j];
|
||||
bufferLengths[i] = bufferLengths[j];
|
||||
i = j;
|
||||
}
|
||||
bufferDatas[max] = data;
|
||||
bufferTimes[max] = getFloat(pcm) / sampleRate;
|
||||
bufferLengths[max] = decoded;
|
||||
requestBuffers++;
|
||||
}
|
||||
}
|
||||
|
||||
inline function flushBuffers() {
|
||||
var i = STREAM_MAX_BUFFERS - (requestBuffers - queuedBuffers);
|
||||
while (queuedBuffers < STREAM_MAX_FLUSH_BUFFERS && queuedBuffers < requestBuffers) {
|
||||
AL.bufferData(buffers[nextBuffer], format, bufferDatas[i], bufferLengths[i], sampleRate);
|
||||
AL.sourceQueueBuffer(source, buffers[nextBuffer]);
|
||||
if (++nextBuffer == STREAM_MAX_FLUSH_BUFFERS) nextBuffer = 0;
|
||||
queuedBuffers++;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
inline function skipBuffers(n:Int) {
|
||||
queuedBuffers -= (n = AL.sourceUnqueueBuffers(source, n).length);
|
||||
requestBuffers -= n;
|
||||
}
|
||||
|
||||
function snapBuffersToTime(time:Float, force:Bool) {
|
||||
if (source == null || parent.buffer == null || parent.buffer.__srcVorbisFile == null) return;
|
||||
|
||||
streamMutex.acquire();
|
||||
|
||||
final sec = time / 1000;
|
||||
if (!force) {
|
||||
var bufferTime:Float;
|
||||
for (i in (STREAM_MAX_BUFFERS - requestBuffers)...(STREAM_MAX_BUFFERS - STREAM_MIN_BUFFERS))
|
||||
if (sec >= (bufferTime = bufferTimes[i]) && sec < bufferTime + (bufferLengths[i] / wordSize / channels / sampleRate))
|
||||
{
|
||||
skipBuffers(i - STREAM_MAX_BUFFERS + requestBuffers);
|
||||
AL.sourcei(source, AL.SAMPLE_OFFSET, Math.floor((sec - bufferTime) * sampleRate));
|
||||
return streamMutex.release();
|
||||
}
|
||||
}
|
||||
|
||||
AL.sourceUnqueueBuffers(source, AL.getSourcei(source, AL.BUFFERS_QUEUED));
|
||||
|
||||
streamEnded = false;
|
||||
streamSeek(Int64.fromFloat(sec * sampleRate));
|
||||
|
||||
requestBuffers = queuedBuffers = streamLoops = nextBuffer = 0;
|
||||
fillBuffers(STREAM_MIN_BUFFERS);
|
||||
flushBuffers();
|
||||
streamMutex.release();
|
||||
}
|
||||
|
||||
static function streamBuffersUpdate() {
|
||||
streamMutex.acquire();
|
||||
|
||||
var i:Int = streamSources.length, source:NativeAudioSource, process:Int, v:Int;
|
||||
while (i-- > 0) {
|
||||
if ((source = streamSources[i]).streamRemove) continue;
|
||||
else if (source.parent.buffer == null) {
|
||||
source.stopStream();
|
||||
continue;
|
||||
}
|
||||
|
||||
process = source.requestBuffers < STREAM_MIN_BUFFERS ? STREAM_MIN_BUFFERS - source.requestBuffers : 0;
|
||||
process = STREAM_PROCESS_BUFFERS > process ? STREAM_PROCESS_BUFFERS : process;
|
||||
if ((process = (v = STREAM_MAX_BUFFERS - source.requestBuffers) > process ? process : v) > 0) source.fillBuffers(process);
|
||||
}
|
||||
|
||||
streamMutex.release();
|
||||
}
|
||||
|
||||
#if !ALLOW_MULTITHREADING
|
||||
static function streamThreadRun() {
|
||||
while (Thread.readMessage(true)) streamBuffersUpdate();
|
||||
threadRunning = false;
|
||||
}
|
||||
#end
|
||||
|
||||
static function streamUpdate() {
|
||||
if (!streamMutex.tryAcquire()) return;
|
||||
|
||||
var i = queuedStreamSources.length, source:NativeAudioSource;
|
||||
while (i-- > 0) streamSources.push(queuedStreamSources[i]);
|
||||
queuedStreamSources.resize(0);
|
||||
|
||||
i = streamSources.length;
|
||||
while (i-- > 0) {
|
||||
if ((source = streamSources[i]).streamRemove || source.source == null) source.removeStream();
|
||||
else {
|
||||
source.skipBuffers(AL.getSourcei(source.source, AL.BUFFERS_PROCESSED));
|
||||
source.flushBuffers();
|
||||
if (AL.getSourcei(source.source, AL.SOURCE_STATE) == AL.STOPPED) {
|
||||
AL.sourcePlay(source.source);
|
||||
source.updateCompleteTimer();
|
||||
}
|
||||
if (source.streamEnded && source.requestBuffers == source.queuedBuffers) source.removeStream();
|
||||
}
|
||||
}
|
||||
|
||||
#if ALLOW_MULTITHREADING
|
||||
if (streamSources.length != 0) funkin.backend.utils.ThreadUtil.execAsync(streamBuffersUpdate);
|
||||
#else
|
||||
if (streamSources.length == 0) {
|
||||
if (wasEmpty) {
|
||||
wasEmpty = false;
|
||||
streamTimer.stop();
|
||||
if (threadRunning) streamThread.sendMessage(1);
|
||||
}
|
||||
else {
|
||||
wasEmpty = true;
|
||||
streamTimer = resetTimer(streamTimer, 1000, streamUpdate);
|
||||
}
|
||||
}
|
||||
else {
|
||||
wasEmpty = false;
|
||||
if (threadRunning || (threadRunning = (streamThread = Thread.create(streamThreadRun)) != null))
|
||||
streamThread.sendMessage(1);
|
||||
}
|
||||
#end
|
||||
|
||||
streamMutex.release();
|
||||
}
|
||||
|
||||
function removeStream() {
|
||||
streamRemove = false;
|
||||
queuedStreamSources.remove(this);
|
||||
streamSources.remove(this);
|
||||
}
|
||||
|
||||
function stopStream() {
|
||||
streamRemove = true;
|
||||
queuedStreamSources.remove(this);
|
||||
}
|
||||
|
||||
function resetStream() {
|
||||
streamRemove = false;
|
||||
if (!queuedStreamSources.contains(this) && !streamSources.contains(this)) {
|
||||
queuedStreamSources.push(this);
|
||||
if (streamTimer == null || !streamTimer.mRunning) streamTimer = resetTimer(streamTimer, 0, streamUpdate);
|
||||
}
|
||||
}
|
||||
|
||||
function timer_onRun() {
|
||||
final pitch = getPitch();
|
||||
var timeRemaining = (getLength() - getCurrentTime()) / pitch;
|
||||
if (timeRemaining > 50 && AL.getSourcei(source, AL.SOURCE_STATE) == AL.PLAYING && (!streamed || !streamEnded && streamLoops <= 0)) {
|
||||
completeTimer = resetTimer(completeTimer, timeRemaining, timer_onRun);
|
||||
return;
|
||||
}
|
||||
|
||||
completeTimer.stop();
|
||||
|
||||
if (loops == 0) return complete();
|
||||
|
||||
if (streamLoops > 0) {
|
||||
loops -= streamLoops;
|
||||
streamLoops = 0;
|
||||
completeTimer = resetTimer(completeTimer, (getLength() + parent.offset - loopTime) / pitch, timer_onRun);
|
||||
}
|
||||
else if (!loopPointsSupported || AL.getSourcei(source, AL.LOOPING) == AL.FALSE) {
|
||||
loops--;
|
||||
playing = true;
|
||||
setCurrentTime(loopTime - parent.offset);
|
||||
}
|
||||
|
||||
if (loops <= 0) {
|
||||
loops = 0;
|
||||
AL.sourcei(source, AL.LOOPING, AL.FALSE);
|
||||
}
|
||||
|
||||
parent.onLoop.dispatch();
|
||||
}
|
||||
|
||||
function updateCompleteTimer() {
|
||||
if (playing) {
|
||||
var timeRemaining = (getLength() - getCurrentTime()) / getPitch();
|
||||
if (timeRemaining > 50) completeTimer = resetTimer(completeTimer, timeRemaining, timer_onRun);
|
||||
else {
|
||||
if (completeTimer != null) completeTimer.stop();
|
||||
if (loops > 0) play();
|
||||
else complete();
|
||||
}
|
||||
}
|
||||
else if (completeTimer != null)
|
||||
completeTimer.stop();
|
||||
}
|
||||
|
||||
public function play() {
|
||||
if (playing || disposed) return;
|
||||
final time = completed ? 0 : getCurrentTime();
|
||||
playing = true;
|
||||
setCurrentTime(time);
|
||||
}
|
||||
|
||||
public function pause() {
|
||||
if (!disposed) AL.sourcePause(source);
|
||||
lastTime = getCurrentTime();
|
||||
playing = false;
|
||||
stopStream();
|
||||
if (completeTimer != null) completeTimer.stop();
|
||||
}
|
||||
|
||||
public function stop() {
|
||||
if (!disposed) AL.sourceStop(source);
|
||||
lastTime = 0;
|
||||
streamLoops = 0;
|
||||
playing = false;
|
||||
stopStream();
|
||||
if (completeTimer != null) completeTimer.stop();
|
||||
}
|
||||
|
||||
public function complete() {
|
||||
if (!completed) parent.onComplete.dispatch();
|
||||
stop();
|
||||
completed = true;
|
||||
}
|
||||
|
||||
public function getCurrentTime():Float {
|
||||
if (disposed) return 0.0;
|
||||
else if (completed) return getLength();
|
||||
else if (!playing) return lastTime - parent.offset;
|
||||
|
||||
var time = AL.getSourcef(source, AL.SEC_OFFSET);
|
||||
if (streamed) {
|
||||
if (playing && streamEnded && AL.getSourcei(source, AL.SOURCE_STATE) == AL.STOPPED) {
|
||||
complete();
|
||||
return getLength();
|
||||
}
|
||||
else if (bufferTimes != null)
|
||||
time += bufferTimes[STREAM_MAX_BUFFERS - requestBuffers];
|
||||
}
|
||||
time *= 1000;
|
||||
|
||||
var length = getRealLength();
|
||||
return if (loops <= 0 || time <= length) time - parent.offset;
|
||||
else ((time - loopTime) % (length - loopTime)) + loopTime - parent.offset;
|
||||
}
|
||||
|
||||
public function setCurrentTime(value:Float):Float {
|
||||
if (disposed) return 0.0;
|
||||
|
||||
final length = getRealLength();
|
||||
value = Math.isFinite(value) ? Math.max(Math.min(value + parent.offset, length), parent.offset) : parent.offset;
|
||||
|
||||
if (streamed) AL.sourceStop(source);
|
||||
else AL.sourcef(source, AL.SEC_OFFSET, value / 1000);
|
||||
|
||||
final timeRemaining = (length - value) / getPitch();
|
||||
if (playing) {
|
||||
if (timeRemaining < 8 && value > 8) complete();
|
||||
else {
|
||||
completed = false;
|
||||
if (streamed) {
|
||||
snapBuffersToTime(value, false);
|
||||
if (!streamEnded) resetStream();
|
||||
}
|
||||
if (AL.getSourcei(source, AL.SOURCE_STATE) != AL.PLAYING) AL.sourcePlay(source);
|
||||
completeTimer = resetTimer(completeTimer, timeRemaining, timer_onRun);
|
||||
}
|
||||
}
|
||||
else {
|
||||
completed = timeRemaining < 8;
|
||||
lastTime = value;
|
||||
if (completeTimer != null) completeTimer.stop();
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public function getPitch():Float {
|
||||
return if (disposed) 1;
|
||||
else AL.getSourcef(source, AL.PITCH);
|
||||
}
|
||||
|
||||
public function setPitch(value:Float):Float {
|
||||
if (disposed || (value = Math.max(value, 0)) == AL.getSourcef(source, AL.PITCH)) return value;
|
||||
AL.sourcef(source, AL.PITCH, value);
|
||||
updateCompleteTimer();
|
||||
return value;
|
||||
}
|
||||
|
||||
public function getGain():Float {
|
||||
return if (disposed) 1;
|
||||
else AL.getSourcef(source, AL.GAIN);
|
||||
}
|
||||
|
||||
public function setGain(value:Float):Float {
|
||||
value = Math.max(value, 0);
|
||||
if (!disposed) AL.sourcef(source, AL.GAIN, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
public function getLoops():Int return loops;
|
||||
|
||||
public function setLoops(value:Int):Int {
|
||||
if (loops == (loops = value < 0 ? 0 : value)) return loops;
|
||||
updateLoopPoints();
|
||||
return loops;
|
||||
}
|
||||
|
||||
public function getLoopTime():Float return loopTime - parent.offset;
|
||||
|
||||
public function setLoopTime(value:Float):Float {
|
||||
if (loopTime == (loopTime = Math.max(Math.min(value + parent.offset, duration), 0))) return loopTime - parent.offset;
|
||||
if ((loopPoints[0] = Std.int(value / 1000 * sampleRate)) >= samples) loopPoints[0] = samples - 1;
|
||||
updateLoopPoints();
|
||||
return loopTime - parent.offset;
|
||||
}
|
||||
|
||||
public function getRealLength():Float return if (endTime == null) duration; else endTime;
|
||||
public function getLength():Float return if (disposed) 0; else (inline getRealLength()) - parent.offset;
|
||||
|
||||
public function setLength(value:Null<Float>):Null<Float> {
|
||||
if (endTime == (endTime = (value == null ? value : Math.max(Math.min(value + parent.offset, duration), 0)))) return endTime - parent.offset;
|
||||
if ((loopPoints[1] = Std.int(getRealLength() / 1000 * sampleRate)) >= samples) loopPoints[1] = samples - 1;
|
||||
updateLoopPoints();
|
||||
return endTime - parent.offset;
|
||||
}
|
||||
|
||||
public function getLatency():Float {
|
||||
//#if (lime >= "8.4.0")
|
||||
//if (latencyExtensionSupported) {
|
||||
// final offsets = AL.getSourcedvSOFT(source, AL.SEC_OFFSET_LATENCY_SOFT, 2);
|
||||
// if (offsets != null) return offsets[1] * 1000;
|
||||
//}
|
||||
//#end
|
||||
return 0;
|
||||
}
|
||||
|
||||
public function getAngles():Vector2 {
|
||||
if (angles == null) angles = new Vector2(Math.PI / 6, -Math.PI / 6);
|
||||
return angles;
|
||||
}
|
||||
|
||||
public function setAngles(left:Float, right:Float):Vector2 {
|
||||
if (angles == null) angles = new Vector2(left, right);
|
||||
else angles.setTo(left, right);
|
||||
|
||||
if (!disposed && stereoAnglesExtensionSupported) {
|
||||
anglesArray[0] = angles.x;
|
||||
anglesArray[1] = angles.y;
|
||||
AL.sourcei(source, 0x1214/*AL.SOURCE_SPATIALIZE_SOFT*/, AL.FALSE);
|
||||
AL.sourcefv(source, 0x1030/*AL.STEREO_ANGLES*/, anglesArray);
|
||||
AL.source3f(source, AL.POSITION, 0, 0, 0);
|
||||
}
|
||||
return angles;
|
||||
}
|
||||
|
||||
public function getPosition():Vector4 {
|
||||
if (position == null) position = new Vector4();
|
||||
return position;
|
||||
}
|
||||
|
||||
public function setPosition(value:Vector4):Vector4 {
|
||||
position.x = value.x;
|
||||
position.y = value.y;
|
||||
position.z = value.z;
|
||||
position.w = value.w;
|
||||
|
||||
// OpenAL Soft Positions doesn't seem to do anything but panning?
|
||||
if (!disposed) {
|
||||
if (stereoAnglesExtensionSupported) AL.sourcei(source, 0x1214/*AL.SOURCE_SPATIALIZE_SOFT*/, Math.abs(position.x) > 1e-04 ? AL.TRUE : AL.FALSE);
|
||||
AL.sourcei(source, AL.MAX_DISTANCE, 1);
|
||||
AL.source3f(source, AL.POSITION, position.x, position.y, position.z);
|
||||
}
|
||||
return position;
|
||||
}
|
||||
|
||||
public function getPan():Float return getPosition().x;
|
||||
|
||||
public function setPan(value:Float):Float {
|
||||
getPosition().setTo(value, 0, -Math.sqrt(1 - value * value));
|
||||
if (!disposed) {
|
||||
if (parent.buffer.channels > 1)
|
||||
setAngles(Math.PI * (Math.min(-value * 2 + 1, 1)) / 6, -Math.PI * Math.min(value * 2 + 1, 1) / 6);
|
||||
else
|
||||
setPosition(position);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@@ -1,766 +0,0 @@
|
||||
package lime._internal.backend.native;
|
||||
|
||||
import haxe.io.Bytes;
|
||||
import lime._internal.backend.native.NativeCFFI;
|
||||
import lime.app.Application;
|
||||
import lime.graphics.cairo.Cairo;
|
||||
import lime.graphics.cairo.CairoFormat;
|
||||
import lime.graphics.cairo.CairoImageSurface;
|
||||
import lime.graphics.cairo.CairoSurface;
|
||||
import lime.graphics.opengl.GL;
|
||||
import lime.graphics.CairoRenderContext;
|
||||
import lime.graphics.Image;
|
||||
import lime.graphics.ImageBuffer;
|
||||
import lime.graphics.OpenGLRenderContext;
|
||||
import lime.graphics.RenderContext;
|
||||
import lime.math.Rectangle;
|
||||
import lime.math.Vector2;
|
||||
import lime.system.Display;
|
||||
import lime.system.DisplayMode;
|
||||
import lime.system.JNI;
|
||||
import lime.system.System;
|
||||
import lime.ui.MouseCursor;
|
||||
import lime.ui.Window;
|
||||
import lime.utils.UInt8Array;
|
||||
|
||||
#if !lime_debug
|
||||
@:fileXml('tags="haxe,release"')
|
||||
@:noDebug
|
||||
#end
|
||||
@:access(lime._internal.backend.native.NativeCFFI)
|
||||
@:access(lime._internal.backend.native.NativeOpenGLRenderContext)
|
||||
@:access(lime.app.Application)
|
||||
@:access(lime.graphics.cairo.Cairo)
|
||||
@:access(lime.graphics.opengl.GL)
|
||||
@:access(lime.graphics.OpenGLRenderContext)
|
||||
@:access(lime.graphics.RenderContext)
|
||||
@:access(lime.system.DisplayMode)
|
||||
@:access(lime.ui.Window)
|
||||
class NativeWindow
|
||||
{
|
||||
public var handle:Dynamic;
|
||||
|
||||
private var closing:Bool;
|
||||
private var cursor:MouseCursor;
|
||||
private var displayMode:DisplayMode;
|
||||
private var frameRate:Float;
|
||||
private var mouseLock:Bool;
|
||||
private var parent:Window;
|
||||
private var useHardware:Bool;
|
||||
#if lime_cairo
|
||||
private var cacheLock:Dynamic;
|
||||
private var cairo:Cairo;
|
||||
private var primarySurface:CairoSurface;
|
||||
#end
|
||||
|
||||
public function new(parent:Window)
|
||||
{
|
||||
this.parent = parent;
|
||||
|
||||
cursor = DEFAULT;
|
||||
displayMode = new DisplayMode(0, 0, 0, 0);
|
||||
|
||||
var attributes = parent.__attributes;
|
||||
var contextAttributes = Reflect.hasField(attributes, "context") ? attributes.context : {};
|
||||
var title = Reflect.hasField(attributes, "title") ? attributes.title : "Lime Application";
|
||||
var flags = 0;
|
||||
|
||||
if (!Reflect.hasField(contextAttributes, "antialiasing")) contextAttributes.antialiasing = 0;
|
||||
if (!Reflect.hasField(contextAttributes, "background")) contextAttributes.background = 0;
|
||||
if (!Reflect.hasField(contextAttributes, "colorDepth")) contextAttributes.colorDepth = 24;
|
||||
if (!Reflect.hasField(contextAttributes, "depth")) contextAttributes.depth = true;
|
||||
if (!Reflect.hasField(contextAttributes, "hardware")) contextAttributes.hardware = true;
|
||||
if (!Reflect.hasField(contextAttributes, "stencil")) contextAttributes.stencil = true;
|
||||
if (!Reflect.hasField(contextAttributes, "vsync")) contextAttributes.vsync = false;
|
||||
|
||||
#if (cairo || (!lime_opengl && !lime_opengles))
|
||||
contextAttributes.type = CAIRO;
|
||||
#end
|
||||
if (Reflect.hasField(contextAttributes, "type") && contextAttributes.type == CAIRO) contextAttributes.hardware = false;
|
||||
|
||||
if (Reflect.hasField(attributes, "allowHighDPI") && attributes.allowHighDPI) flags |= cast WindowFlags.WINDOW_FLAG_ALLOW_HIGHDPI;
|
||||
if (Reflect.hasField(attributes, "alwaysOnTop") && attributes.alwaysOnTop) flags |= cast WindowFlags.WINDOW_FLAG_ALWAYS_ON_TOP;
|
||||
if (Reflect.hasField(attributes, "borderless") && attributes.borderless) flags |= cast WindowFlags.WINDOW_FLAG_BORDERLESS;
|
||||
if (Reflect.hasField(attributes, "fullscreen") && attributes.fullscreen) flags |= cast WindowFlags.WINDOW_FLAG_FULLSCREEN;
|
||||
if (Reflect.hasField(attributes, "hidden") && attributes.hidden) flags |= cast WindowFlags.WINDOW_FLAG_HIDDEN;
|
||||
if (Reflect.hasField(attributes, "maximized") && attributes.maximized) flags |= cast WindowFlags.WINDOW_FLAG_MAXIMIZED;
|
||||
if (Reflect.hasField(attributes, "minimized") && attributes.minimized) flags |= cast WindowFlags.WINDOW_FLAG_MINIMIZED;
|
||||
if (Reflect.hasField(attributes, "resizable") && attributes.resizable) flags |= cast WindowFlags.WINDOW_FLAG_RESIZABLE;
|
||||
|
||||
if (contextAttributes.antialiasing >= 4)
|
||||
{
|
||||
flags |= cast WindowFlags.WINDOW_FLAG_HW_AA_HIRES;
|
||||
}
|
||||
else if (contextAttributes.antialiasing >= 2)
|
||||
{
|
||||
flags |= cast WindowFlags.WINDOW_FLAG_HW_AA;
|
||||
}
|
||||
|
||||
if (contextAttributes.colorDepth == 32) flags |= cast WindowFlags.WINDOW_FLAG_COLOR_DEPTH_32_BIT;
|
||||
if (contextAttributes.depth) flags |= cast WindowFlags.WINDOW_FLAG_DEPTH_BUFFER;
|
||||
if (contextAttributes.hardware) flags |= cast WindowFlags.WINDOW_FLAG_HARDWARE;
|
||||
if (contextAttributes.stencil) flags |= cast WindowFlags.WINDOW_FLAG_STENCIL_BUFFER;
|
||||
if (contextAttributes.vsync) flags |= cast WindowFlags.WINDOW_FLAG_VSYNC;
|
||||
|
||||
var width = Reflect.hasField(attributes, "width") ? attributes.width : #if desktop 800 #else 0 #end;
|
||||
var height = Reflect.hasField(attributes, "height") ? attributes.height : #if desktop 600 #else 0 #end;
|
||||
|
||||
#if (!macro && lime_cffi)
|
||||
handle = NativeCFFI.lime_window_create(parent.application.__backend.handle, width, height, flags, title);
|
||||
|
||||
#if (DARK_MODE_WINDOW && !macro)
|
||||
funkin.backend.utils.NativeAPI.setDarkMode(title, true);
|
||||
#end
|
||||
|
||||
if (handle != null)
|
||||
{
|
||||
parent.__width = NativeCFFI.lime_window_get_width(handle);
|
||||
parent.__height = NativeCFFI.lime_window_get_height(handle);
|
||||
parent.__x = NativeCFFI.lime_window_get_x(handle);
|
||||
parent.__y = NativeCFFI.lime_window_get_y(handle);
|
||||
parent.__hidden = (Reflect.hasField(attributes, "hidden") && attributes.hidden);
|
||||
parent.id = NativeCFFI.lime_window_get_id(handle);
|
||||
}
|
||||
|
||||
parent.__scale = NativeCFFI.lime_window_get_scale(handle);
|
||||
|
||||
var context = new RenderContext();
|
||||
context.window = parent;
|
||||
|
||||
#if hl
|
||||
var contextType = @:privateAccess String.fromUTF8(NativeCFFI.lime_window_get_context_type(handle));
|
||||
#else
|
||||
var contextType:String = NativeCFFI.lime_window_get_context_type(handle);
|
||||
#end
|
||||
|
||||
switch (contextType)
|
||||
{
|
||||
case "opengl":
|
||||
var gl = new NativeOpenGLRenderContext();
|
||||
|
||||
useHardware = true;
|
||||
|
||||
#if lime_opengl
|
||||
context.gl = gl;
|
||||
#end
|
||||
|
||||
context.gles2 = gl;
|
||||
context.webgl = gl;
|
||||
context.type = gl.type;
|
||||
context.version = Std.string(gl.version);
|
||||
|
||||
if (gl.type == OPENGLES && gl.version >= 3)
|
||||
{
|
||||
context.gles3 = gl;
|
||||
context.webgl2 = gl;
|
||||
}
|
||||
|
||||
if (GL.context == null)
|
||||
{
|
||||
GL.context = gl;
|
||||
}
|
||||
|
||||
default:
|
||||
useHardware = false;
|
||||
|
||||
#if lime_cairo
|
||||
context.cairo = cairo;
|
||||
context.type = CAIRO;
|
||||
context.version = "";
|
||||
|
||||
parent.context = context;
|
||||
render();
|
||||
#end
|
||||
context.type = CAIRO;
|
||||
}
|
||||
|
||||
contextAttributes.type = context.type;
|
||||
context.attributes = contextAttributes;
|
||||
parent.context = context;
|
||||
|
||||
setFrameRate(Reflect.hasField(attributes, "frameRate") ? attributes.frameRate : 60);
|
||||
#end
|
||||
}
|
||||
|
||||
public function alert(message:String, title:String):Void
|
||||
{
|
||||
if (handle != null)
|
||||
{
|
||||
#if (!macro && lime_cffi)
|
||||
NativeCFFI.lime_window_alert(handle, message, title);
|
||||
#end
|
||||
}
|
||||
}
|
||||
|
||||
public function close():Void
|
||||
{
|
||||
if (!closing)
|
||||
{
|
||||
closing = true;
|
||||
parent.onClose.dispatch();
|
||||
|
||||
if (!parent.onClose.canceled)
|
||||
{
|
||||
if (handle != null)
|
||||
{
|
||||
#if (!macro && lime_cffi)
|
||||
NativeCFFI.lime_window_close(handle);
|
||||
#end
|
||||
handle = null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
closing = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function contextFlip():Void
|
||||
{
|
||||
#if (!macro && lime_cffi)
|
||||
if (!useHardware)
|
||||
{
|
||||
#if lime_cairo
|
||||
if (cairo != null)
|
||||
{
|
||||
primarySurface.flush();
|
||||
}
|
||||
#end
|
||||
NativeCFFI.lime_window_context_unlock(handle);
|
||||
}
|
||||
|
||||
NativeCFFI.lime_window_context_flip(handle);
|
||||
#end
|
||||
}
|
||||
|
||||
public function focus():Void
|
||||
{
|
||||
if (handle != null)
|
||||
{
|
||||
#if (!macro && lime_cffi)
|
||||
NativeCFFI.lime_window_focus(handle);
|
||||
#end
|
||||
}
|
||||
}
|
||||
|
||||
public function getCursor():MouseCursor
|
||||
{
|
||||
return cursor;
|
||||
}
|
||||
|
||||
public function getDisplay():Display
|
||||
{
|
||||
if (handle != null)
|
||||
{
|
||||
#if (!macro && lime_cffi)
|
||||
var index = NativeCFFI.lime_window_get_display(handle);
|
||||
|
||||
if (index > -1)
|
||||
{
|
||||
return System.getDisplay(index);
|
||||
}
|
||||
#end
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getDisplayMode():DisplayMode
|
||||
{
|
||||
if (handle != null)
|
||||
{
|
||||
#if (!macro && lime_cffi)
|
||||
#if hl
|
||||
NativeCFFI.lime_window_get_display_mode(handle, displayMode);
|
||||
#else
|
||||
var data:Dynamic = NativeCFFI.lime_window_get_display_mode(handle);
|
||||
displayMode.width = data.width;
|
||||
displayMode.height = data.height;
|
||||
displayMode.pixelFormat = data.pixelFormat;
|
||||
displayMode.refreshRate = data.refreshRate;
|
||||
#end
|
||||
#end
|
||||
}
|
||||
|
||||
return displayMode;
|
||||
}
|
||||
|
||||
public function getFrameRate():Float
|
||||
{
|
||||
return frameRate;
|
||||
}
|
||||
|
||||
public function getMouseLock():Bool
|
||||
{
|
||||
if (handle != null)
|
||||
{
|
||||
#if (!macro && lime_cffi)
|
||||
mouseLock = NativeCFFI.lime_window_get_mouse_lock(handle);
|
||||
#end
|
||||
}
|
||||
|
||||
return mouseLock;
|
||||
}
|
||||
|
||||
public function getTextInputEnabled():Bool
|
||||
{
|
||||
if (handle != null)
|
||||
{
|
||||
#if (!macro && lime_cffi)
|
||||
return NativeCFFI.lime_window_get_text_input_enabled(handle);
|
||||
#end
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function move(x:Int, y:Int):Void
|
||||
{
|
||||
if (handle != null)
|
||||
{
|
||||
#if (!macro && lime_cffi)
|
||||
NativeCFFI.lime_window_move(handle, x, y);
|
||||
#end
|
||||
}
|
||||
}
|
||||
|
||||
public function readPixels(rect:Rectangle):Image
|
||||
{
|
||||
var imageBuffer:ImageBuffer = null;
|
||||
|
||||
switch (parent.context.type)
|
||||
{
|
||||
case OPENGL, OPENGLES, WEBGL:
|
||||
var gl = parent.context.webgl;
|
||||
var windowWidth = Std.int(parent.__width * parent.__scale);
|
||||
var windowHeight = Std.int(parent.__height * parent.__scale);
|
||||
|
||||
var x, y, width, height;
|
||||
|
||||
if (rect != null)
|
||||
{
|
||||
x = Std.int(rect.x);
|
||||
y = Std.int((windowHeight - rect.y) - rect.height);
|
||||
width = Std.int(rect.width);
|
||||
height = Std.int(rect.height);
|
||||
}
|
||||
else
|
||||
{
|
||||
x = 0;
|
||||
y = 0;
|
||||
width = windowWidth;
|
||||
height = windowHeight;
|
||||
}
|
||||
|
||||
var data = new UInt8Array(width * height * 4);
|
||||
|
||||
gl.readPixels(x, y, width, height, gl.RGBA, gl.UNSIGNED_BYTE, data);
|
||||
|
||||
#if !js // TODO
|
||||
|
||||
var rowLength = width * 4;
|
||||
var srcPosition = (height - 1) * rowLength;
|
||||
var destPosition = 0;
|
||||
|
||||
var temp = Bytes.alloc(rowLength);
|
||||
var buffer = data.buffer;
|
||||
var rows = Std.int(height / 2);
|
||||
|
||||
while (rows-- > 0)
|
||||
{
|
||||
temp.blit(0, buffer, destPosition, rowLength);
|
||||
buffer.blit(destPosition, buffer, srcPosition, rowLength);
|
||||
buffer.blit(srcPosition, temp, 0, rowLength);
|
||||
|
||||
destPosition += rowLength;
|
||||
srcPosition -= rowLength;
|
||||
}
|
||||
#end
|
||||
|
||||
imageBuffer = new ImageBuffer(data, width, height, 32, RGBA32);
|
||||
|
||||
default:
|
||||
#if (!macro && lime_cffi)
|
||||
#if !cs
|
||||
imageBuffer = NativeCFFI.lime_window_read_pixels(handle, rect, new ImageBuffer(new UInt8Array(Bytes.alloc(0))));
|
||||
#else
|
||||
var data:Dynamic = NativeCFFI.lime_window_read_pixels(handle, rect, null);
|
||||
if (data != null)
|
||||
{
|
||||
imageBuffer = new ImageBuffer(new UInt8Array(@:privateAccess new Bytes(data.data.length, data.data.b)), data.width, data.height,
|
||||
data.bitsPerPixel);
|
||||
}
|
||||
#end
|
||||
#end
|
||||
|
||||
if (imageBuffer != null)
|
||||
{
|
||||
imageBuffer.format = RGBA32;
|
||||
}
|
||||
}
|
||||
|
||||
if (imageBuffer != null)
|
||||
{
|
||||
return new Image(imageBuffer);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function render():Void
|
||||
{
|
||||
#if (!macro && lime_cffi)
|
||||
NativeCFFI.lime_window_context_make_current(handle);
|
||||
|
||||
if (!useHardware)
|
||||
{
|
||||
#if lime_cairo
|
||||
var lock:Dynamic = NativeCFFI.lime_window_context_lock(handle);
|
||||
|
||||
if (lock != null
|
||||
&& (cacheLock == null || cacheLock.pixels != lock.pixels || cacheLock.width != lock.width || cacheLock.height != lock.height))
|
||||
{
|
||||
primarySurface = CairoImageSurface.create(lock.pixels, CairoFormat.ARGB32, lock.width, lock.height, lock.pitch);
|
||||
|
||||
if (cairo != null)
|
||||
{
|
||||
cairo.recreate(primarySurface);
|
||||
}
|
||||
else
|
||||
{
|
||||
cairo = new Cairo(primarySurface);
|
||||
}
|
||||
|
||||
parent.context.cairo = cairo;
|
||||
}
|
||||
|
||||
cacheLock = lock;
|
||||
#else
|
||||
parent.context = null;
|
||||
#end
|
||||
}
|
||||
#end
|
||||
}
|
||||
|
||||
public function resize(width:Int, height:Int):Void
|
||||
{
|
||||
if (handle != null)
|
||||
{
|
||||
#if (!macro && lime_cffi)
|
||||
NativeCFFI.lime_window_resize(handle, width, height);
|
||||
#end
|
||||
}
|
||||
}
|
||||
|
||||
#if (lime >= "8.1.0")
|
||||
public function setMinSize(width:Int, height:Int):Void
|
||||
{
|
||||
if (handle != null)
|
||||
{
|
||||
#if (!macro && lime_cffi)
|
||||
NativeCFFI.lime_window_set_minimum_size(handle, width, height);
|
||||
#end
|
||||
}
|
||||
}
|
||||
|
||||
public function setMaxSize(width:Int, height:Int):Void
|
||||
{
|
||||
if (handle != null)
|
||||
{
|
||||
#if (!macro && lime_cffi)
|
||||
NativeCFFI.lime_window_set_maximum_size(handle, width, height);
|
||||
#end
|
||||
}
|
||||
}
|
||||
#end
|
||||
|
||||
public function setBorderless(value:Bool):Bool
|
||||
{
|
||||
if (handle != null)
|
||||
{
|
||||
#if (!macro && lime_cffi)
|
||||
NativeCFFI.lime_window_set_borderless(handle, value);
|
||||
#end
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public function setCursor(value:MouseCursor):MouseCursor
|
||||
{
|
||||
if (cursor != value)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
#if (!macro && lime_cffi)
|
||||
NativeCFFI.lime_window_set_cursor(handle, 0);
|
||||
#end
|
||||
}
|
||||
else
|
||||
{
|
||||
var type:MouseCursorType = switch (value)
|
||||
{
|
||||
case ARROW: ARROW;
|
||||
case CROSSHAIR: CROSSHAIR;
|
||||
case MOVE: MOVE;
|
||||
case POINTER: POINTER;
|
||||
case RESIZE_NESW: RESIZE_NESW;
|
||||
case RESIZE_NS: RESIZE_NS;
|
||||
case RESIZE_NWSE: RESIZE_NWSE;
|
||||
case RESIZE_WE: RESIZE_WE;
|
||||
case TEXT: TEXT;
|
||||
case WAIT: WAIT;
|
||||
case WAIT_ARROW: WAIT_ARROW;
|
||||
default: DEFAULT;
|
||||
}
|
||||
|
||||
#if (!macro && lime_cffi)
|
||||
NativeCFFI.lime_window_set_cursor(handle, type);
|
||||
#end
|
||||
}
|
||||
|
||||
cursor = value;
|
||||
}
|
||||
|
||||
return cursor;
|
||||
}
|
||||
|
||||
public function setDisplayMode(value:DisplayMode):DisplayMode
|
||||
{
|
||||
if (handle != null)
|
||||
{
|
||||
#if (!macro && lime_cffi)
|
||||
#if hl
|
||||
NativeCFFI.lime_window_set_display_mode(handle, value, displayMode);
|
||||
#else
|
||||
var data:Dynamic = NativeCFFI.lime_window_set_display_mode(handle, value);
|
||||
displayMode.width = data.width;
|
||||
displayMode.height = data.height;
|
||||
displayMode.pixelFormat = data.pixelFormat;
|
||||
displayMode.refreshRate = data.refreshRate;
|
||||
#end
|
||||
#end
|
||||
}
|
||||
|
||||
return displayMode;
|
||||
}
|
||||
|
||||
public function setMouseLock(value:Bool):Bool
|
||||
{
|
||||
if (mouseLock != value)
|
||||
{
|
||||
#if (!macro && lime_cffi)
|
||||
NativeCFFI.lime_window_set_mouse_lock(handle, value);
|
||||
#end
|
||||
|
||||
mouseLock = value;
|
||||
}
|
||||
|
||||
return mouseLock;
|
||||
}
|
||||
|
||||
public function setTextInputEnabled(value:Bool):Bool
|
||||
{
|
||||
if (handle != null)
|
||||
{
|
||||
#if (!macro && lime_cffi)
|
||||
NativeCFFI.lime_window_set_text_input_enabled(handle, value);
|
||||
#end
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public function setTextInputRect(value:Rectangle):Rectangle
|
||||
{
|
||||
if (handle != null)
|
||||
{
|
||||
#if (!macro && lime_cffi)
|
||||
NativeCFFI.lime_window_set_text_input_rect(handle, value);
|
||||
#end
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public function setFrameRate(value:Float):Float
|
||||
{
|
||||
// TODO: Support multiple independent frame rates per window
|
||||
|
||||
if (handle != null)
|
||||
{
|
||||
#if (!macro && lime_cffi)
|
||||
NativeCFFI.lime_application_set_frame_rate(parent.application.__backend.handle, value);
|
||||
#end
|
||||
}
|
||||
|
||||
return frameRate = value;
|
||||
}
|
||||
|
||||
public function setFullscreen(value:Bool):Bool
|
||||
{
|
||||
if (handle != null)
|
||||
{
|
||||
#if (!macro && lime_cffi)
|
||||
value = NativeCFFI.lime_window_set_fullscreen(handle, value);
|
||||
|
||||
parent.__width = NativeCFFI.lime_window_get_width(handle);
|
||||
parent.__height = NativeCFFI.lime_window_get_height(handle);
|
||||
parent.__x = NativeCFFI.lime_window_get_x(handle);
|
||||
parent.__y = NativeCFFI.lime_window_get_y(handle);
|
||||
#end
|
||||
|
||||
if (value)
|
||||
{
|
||||
parent.onFullscreen.dispatch();
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public function setIcon(image:Image):Void
|
||||
{
|
||||
if (handle != null)
|
||||
{
|
||||
#if (!macro && lime_cffi)
|
||||
NativeCFFI.lime_window_set_icon(handle, image.buffer);
|
||||
#end
|
||||
}
|
||||
}
|
||||
|
||||
public function setMaximized(value:Bool):Bool
|
||||
{
|
||||
if (handle != null)
|
||||
{
|
||||
#if (!macro && lime_cffi)
|
||||
return NativeCFFI.lime_window_set_maximized(handle, value);
|
||||
#end
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public function setMinimized(value:Bool):Bool
|
||||
{
|
||||
if (handle != null)
|
||||
{
|
||||
#if (!macro && lime_cffi)
|
||||
return NativeCFFI.lime_window_set_minimized(handle, value);
|
||||
#end
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public function setResizable(value:Bool):Bool
|
||||
{
|
||||
if (handle != null)
|
||||
{
|
||||
#if (!macro && lime_cffi)
|
||||
NativeCFFI.lime_window_set_resizable(handle, value);
|
||||
|
||||
// TODO: remove need for workaround
|
||||
|
||||
NativeCFFI.lime_window_set_borderless(handle, !parent.__borderless);
|
||||
NativeCFFI.lime_window_set_borderless(handle, parent.__borderless);
|
||||
#end
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public function setTitle(value:String):String
|
||||
{
|
||||
if (handle != null)
|
||||
{
|
||||
#if (!macro && lime_cffi)
|
||||
return NativeCFFI.lime_window_set_title(handle, value);
|
||||
#end
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
#if (lime >= "8.1.0")
|
||||
public function setVisible(value:Bool):Bool
|
||||
{
|
||||
if (handle != null)
|
||||
{
|
||||
#if (!macro && lime_cffi)
|
||||
NativeCFFI.lime_window_set_visible(handle, value);
|
||||
#end
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public function getOpacity():Float
|
||||
{
|
||||
if (handle != null)
|
||||
{
|
||||
#if (!macro && lime_cffi)
|
||||
return NativeCFFI.lime_window_get_opacity(handle);
|
||||
#end
|
||||
}
|
||||
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
public function setOpacity(value:Float):Void
|
||||
{
|
||||
if (handle != null)
|
||||
{
|
||||
#if (!macro && lime_cffi)
|
||||
NativeCFFI.lime_window_set_opacity(handle, value);
|
||||
#end
|
||||
}
|
||||
}
|
||||
#end
|
||||
|
||||
public function warpMouse(x:Int, y:Int):Void
|
||||
{
|
||||
#if (!macro && lime_cffi)
|
||||
NativeCFFI.lime_window_warp_mouse(handle, x, y);
|
||||
#end
|
||||
}
|
||||
}
|
||||
|
||||
enum abstract MouseCursorType(Int) from Int to Int
|
||||
{
|
||||
var HIDDEN = 0;
|
||||
var ARROW = 1;
|
||||
var CROSSHAIR = 2;
|
||||
var DEFAULT = 3;
|
||||
var MOVE = 4;
|
||||
var POINTER = 5;
|
||||
var RESIZE_NESW = 6;
|
||||
var RESIZE_NS = 7;
|
||||
var RESIZE_NWSE = 8;
|
||||
var RESIZE_WE = 9;
|
||||
var TEXT = 10;
|
||||
var WAIT = 11;
|
||||
var WAIT_ARROW = 12;
|
||||
}
|
||||
|
||||
enum abstract WindowFlags(Int)
|
||||
{
|
||||
var WINDOW_FLAG_FULLSCREEN = 0x00000001;
|
||||
var WINDOW_FLAG_BORDERLESS = 0x00000002;
|
||||
var WINDOW_FLAG_RESIZABLE = 0x00000004;
|
||||
var WINDOW_FLAG_HARDWARE = 0x00000008;
|
||||
var WINDOW_FLAG_VSYNC = 0x00000010;
|
||||
var WINDOW_FLAG_HW_AA = 0x00000020;
|
||||
var WINDOW_FLAG_HW_AA_HIRES = 0x00000060;
|
||||
var WINDOW_FLAG_ALLOW_SHADERS = 0x00000080;
|
||||
var WINDOW_FLAG_REQUIRE_SHADERS = 0x00000100;
|
||||
var WINDOW_FLAG_DEPTH_BUFFER = 0x00000200;
|
||||
var WINDOW_FLAG_STENCIL_BUFFER = 0x00000400;
|
||||
var WINDOW_FLAG_ALLOW_HIGHDPI = 0x00000800;
|
||||
var WINDOW_FLAG_HIDDEN = 0x00001000;
|
||||
var WINDOW_FLAG_MINIMIZED = 0x00002000;
|
||||
var WINDOW_FLAG_MAXIMIZED = 0x00004000;
|
||||
var WINDOW_FLAG_ALWAYS_ON_TOP = 0x00008000;
|
||||
var WINDOW_FLAG_COLOR_DEPTH_32_BIT = 0x00010000;
|
||||
}
|
||||
@@ -1,518 +0,0 @@
|
||||
package lime.media;
|
||||
|
||||
import haxe.io.Bytes;
|
||||
import haxe.io.Path;
|
||||
import lime._internal.backend.native.NativeCFFI;
|
||||
import lime._internal.format.Base64;
|
||||
import lime.app.Future;
|
||||
import lime.app.Promise;
|
||||
import lime.media.openal.AL;
|
||||
import lime.media.openal.ALBuffer;
|
||||
#if lime_vorbis
|
||||
import lime.media.vorbis.Vorbis;
|
||||
import lime.media.vorbis.VorbisFile;
|
||||
#end
|
||||
import lime.net.HTTPRequest;
|
||||
import lime.utils.Log;
|
||||
import lime.utils.UInt8Array;
|
||||
#if lime_howlerjs
|
||||
import lime.media.howlerjs.Howl;
|
||||
#end
|
||||
#if (js && html5)
|
||||
import js.html.Audio;
|
||||
#elseif flash
|
||||
import flash.media.Sound;
|
||||
import flash.net.URLRequest;
|
||||
#end
|
||||
|
||||
@:access(lime._internal.backend.native.NativeCFFI)
|
||||
@:access(lime.utils.Assets)
|
||||
#if hl
|
||||
@:keep
|
||||
#end
|
||||
#if !lime_debug
|
||||
@:fileXml('tags="haxe,release"')
|
||||
@:noDebug
|
||||
#end
|
||||
|
||||
/**
|
||||
The `AudioBuffer` class represents a buffer of audio data that can be played back using an `AudioSource`.
|
||||
It supports a variety of audio formats and platforms, providing a consistent API for loading and managing audio data.
|
||||
|
||||
Depending on the platform, the audio backend may differ, but the class provides a unified interface for accessing
|
||||
audio data, whether it's stored in memory, loaded from a file, or streamed.
|
||||
|
||||
@see lime.media.AudioSource
|
||||
**/
|
||||
class AudioBuffer
|
||||
{
|
||||
/**
|
||||
The number of bits per sample in the audio data.
|
||||
**/
|
||||
public var bitsPerSample:Int;
|
||||
|
||||
/**
|
||||
The number of audio channels (e.g., 1 for mono, 2 for stereo).
|
||||
**/
|
||||
public var channels:Int;
|
||||
|
||||
/**
|
||||
The raw audio data stored as a `UInt8Array`.
|
||||
**/
|
||||
public var data:UInt8Array;
|
||||
|
||||
/**
|
||||
The sample rate of the audio data, in Hz.
|
||||
**/
|
||||
public var sampleRate:Int;
|
||||
|
||||
/**
|
||||
The source of the audio data. This can be an `Audio`, `Sound`, `Howl`, or other platform-specific object.
|
||||
**/
|
||||
public var src(get, set):Dynamic;
|
||||
|
||||
@:noCompletion private var __srcAudio:#if (js && html5) Audio #else Dynamic #end;
|
||||
@:noCompletion private var __srcBuffer:#if lime_cffi ALBuffer #else Dynamic #end;
|
||||
@:noCompletion private var __srcCustom:Dynamic;
|
||||
@:noCompletion private var __srcHowl:#if lime_howlerjs Howl #else Dynamic #end;
|
||||
@:noCompletion private var __srcSound:#if flash Sound #else Dynamic #end;
|
||||
@:noCompletion private var __srcVorbisFile:#if lime_vorbis VorbisFile #else Dynamic #end;
|
||||
|
||||
#if commonjs
|
||||
private static function __init__()
|
||||
{
|
||||
var p = untyped AudioBuffer.prototype;
|
||||
untyped Object.defineProperties(p,
|
||||
{
|
||||
"src": {get: p.get_src, set: p.set_src}
|
||||
});
|
||||
}
|
||||
#end
|
||||
|
||||
/**
|
||||
Creates a new, empty `AudioBuffer` instance.
|
||||
**/
|
||||
public function new() {}
|
||||
|
||||
/**
|
||||
Disposes of the resources used by this `AudioBuffer`, such as unloading any associated audio data.
|
||||
**/
|
||||
public function dispose():Void
|
||||
{
|
||||
#if (js && html5 && lime_howlerjs)
|
||||
if (__srcHowl != null) __srcHowl.unload();
|
||||
__srcHowl = null;
|
||||
#end
|
||||
#if lime_cffi
|
||||
if (__srcBuffer != null) {
|
||||
AL.bufferData(__srcBuffer, 0, null, 0, 0);
|
||||
AL.deleteBuffer(__srcBuffer);
|
||||
}
|
||||
__srcBuffer = null;
|
||||
#end
|
||||
#if lime_vorbis
|
||||
if (__srcVorbisFile != null) __srcVorbisFile.clear();
|
||||
__srcVorbisFile = null;
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
Creates an `AudioBuffer` from a Base64-encoded string.
|
||||
|
||||
@param base64String The Base64-encoded audio data.
|
||||
@return An `AudioBuffer` instance with the decoded audio data.
|
||||
**/
|
||||
public static function fromBase64(base64String:String):AudioBuffer
|
||||
{
|
||||
if (base64String == null) return null;
|
||||
|
||||
#if (js && html5 && lime_howlerjs)
|
||||
// if base64String doesn't contain codec data, add it.
|
||||
if (base64String.indexOf(",") == -1)
|
||||
{
|
||||
base64String = "data:" + __getCodec(Base64.decode(base64String)) + ";base64," + base64String;
|
||||
}
|
||||
|
||||
var audioBuffer = new AudioBuffer();
|
||||
audioBuffer.src = new Howl({src: [base64String], preload: false});
|
||||
return audioBuffer;
|
||||
#elseif (lime_cffi && !macro)
|
||||
#if !cs
|
||||
// if base64String contains codec data, strip it then decode it.
|
||||
var base64StringSplit = base64String.split(",");
|
||||
var base64StringNoEncoding = base64StringSplit[base64StringSplit.length - 1];
|
||||
var bytes:Bytes = Base64.decode(base64StringNoEncoding);
|
||||
var audioBuffer = new AudioBuffer();
|
||||
audioBuffer.data = new UInt8Array(Bytes.alloc(0));
|
||||
|
||||
return NativeCFFI.lime_audio_load_bytes(bytes, audioBuffer);
|
||||
#else
|
||||
// if base64String contains codec data, strip it then decode it.
|
||||
var base64StringSplit = base64String.split(",");
|
||||
var base64StringNoEncoding = base64StringSplit[base64StringSplit.length - 1];
|
||||
var bytes:Bytes = Base64.decode(base64StringNoEncoding);
|
||||
var data:Dynamic = NativeCFFI.lime_audio_load_bytes(bytes, null);
|
||||
|
||||
if (data != null)
|
||||
{
|
||||
var audioBuffer = new AudioBuffer();
|
||||
audioBuffer.bitsPerSample = data.bitsPerSample;
|
||||
audioBuffer.channels = data.channels;
|
||||
audioBuffer.data = new UInt8Array(@:privateAccess new Bytes(data.data.length, data.data.b));
|
||||
audioBuffer.sampleRate = data.sampleRate;
|
||||
return audioBuffer;
|
||||
}
|
||||
#end
|
||||
#end
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
Creates an `AudioBuffer` from a `Bytes` object.
|
||||
|
||||
@param bytes The `Bytes` object containing the audio data.
|
||||
@return An `AudioBuffer` instance with the decoded audio data.
|
||||
**/
|
||||
public static function fromBytes(bytes:Bytes):AudioBuffer
|
||||
{
|
||||
if (bytes == null) return null;
|
||||
|
||||
#if (js && html5 && lime_howlerjs)
|
||||
var audioBuffer = new AudioBuffer();
|
||||
audioBuffer.src = new Howl({src: ["data:" + __getCodec(bytes) + ";base64," + Base64.encode(bytes)], preload: false});
|
||||
|
||||
return audioBuffer;
|
||||
#elseif (lime_cffi && !macro)
|
||||
#if lime_vorbis
|
||||
var vorbisFile = VorbisFile.fromBytes(bytes);
|
||||
if (vorbisFile != null) return fromVorbisFile(vorbisFile);
|
||||
#end
|
||||
#if !cs
|
||||
var audioBuffer = new AudioBuffer();
|
||||
audioBuffer.data = new UInt8Array(Bytes.alloc(0));
|
||||
|
||||
return NativeCFFI.lime_audio_load_bytes(bytes, audioBuffer);
|
||||
#else
|
||||
var data:Dynamic = NativeCFFI.lime_audio_load_bytes(bytes, null);
|
||||
|
||||
if (data != null)
|
||||
{
|
||||
var audioBuffer = new AudioBuffer();
|
||||
audioBuffer.bitsPerSample = data.bitsPerSample;
|
||||
audioBuffer.channels = data.channels;
|
||||
audioBuffer.data = new UInt8Array(@:privateAccess new Bytes(data.data.length, data.data.b));
|
||||
audioBuffer.sampleRate = data.sampleRate;
|
||||
return audioBuffer;
|
||||
}
|
||||
#end
|
||||
#end
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
Creates an `AudioBuffer` from a file.
|
||||
|
||||
@param path The file path to the audio data.
|
||||
@return An `AudioBuffer` instance with the audio data loaded from the file.
|
||||
**/
|
||||
public static function fromFile(path:String #if (js && html5 && lime_howlerjs), ?howlHtml5 = false #end):AudioBuffer
|
||||
{
|
||||
if (path == null) return null;
|
||||
|
||||
#if (js && html5 && lime_howlerjs)
|
||||
var audioBuffer = new AudioBuffer();
|
||||
|
||||
#if force_html5_audio
|
||||
audioBuffer.__srcHowl = new Howl({src: [path], html5: true, preload: false});
|
||||
#else
|
||||
audioBuffer.__srcHowl = new Howl({src: [path], html5: howlHtml5, preload: false});
|
||||
#end
|
||||
|
||||
return audioBuffer;
|
||||
#elseif flash
|
||||
switch (Path.extension(path))
|
||||
{
|
||||
case "ogg", "wav":
|
||||
return null;
|
||||
default:
|
||||
}
|
||||
|
||||
var audioBuffer = new AudioBuffer();
|
||||
audioBuffer.__srcSound = new Sound(new URLRequest(path));
|
||||
return audioBuffer;
|
||||
#elseif (lime_cffi && !macro)
|
||||
#if !cs
|
||||
var audioBuffer = new AudioBuffer();
|
||||
audioBuffer.data = new UInt8Array(Bytes.alloc(0));
|
||||
|
||||
//audioBuffer = NativeCFFI.lime_audio_load_file(path, audioBuffer);
|
||||
//if (audioBuffer != null) audioBuffer.initBuffer();
|
||||
//return audioBuffer;
|
||||
return NativeCFFI.lime_audio_load_file(path, audioBuffer);
|
||||
#else
|
||||
var data:Dynamic = NativeCFFI.lime_audio_load_file(path, null);
|
||||
|
||||
if (data != null)
|
||||
{
|
||||
var audioBuffer = new AudioBuffer();
|
||||
audioBuffer.bitsPerSample = data.bitsPerSample;
|
||||
audioBuffer.channels = data.channels;
|
||||
audioBuffer.data = new UInt8Array(@:privateAccess new Bytes(data.data.length, data.data.b));
|
||||
audioBuffer.sampleRate = data.sampleRate;
|
||||
//audioBuffer.initBuffer();
|
||||
return audioBuffer;
|
||||
}
|
||||
|
||||
return null;
|
||||
#end
|
||||
#else
|
||||
return null;
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
Creates an `AudioBuffer` from an array of file paths.
|
||||
|
||||
@param paths An array of file paths to search for audio data.
|
||||
@return An `AudioBuffer` instance with the audio data loaded from the first valid file found.
|
||||
**/
|
||||
public static function fromFiles(paths:Array<String> #if (js && html5 && lime_howlerjs), ?howlHtml5 = false #end):AudioBuffer
|
||||
{
|
||||
#if (js && html5 && lime_howlerjs)
|
||||
var audioBuffer = new AudioBuffer();
|
||||
|
||||
#if force_html5_audio
|
||||
audioBuffer.__srcHowl = new Howl({src: paths, html5: true, preload: false});
|
||||
#else
|
||||
audioBuffer.__srcHowl = new Howl({src: paths, html5: howlHtml5, preload: false});
|
||||
#end
|
||||
|
||||
return audioBuffer;
|
||||
#else
|
||||
var buffer = null;
|
||||
|
||||
for (path in paths)
|
||||
{
|
||||
buffer = AudioBuffer.fromFile(path);
|
||||
if (buffer != null) break;
|
||||
}
|
||||
|
||||
return buffer;
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
Creates an `AudioBuffer` from a `VorbisFile`.
|
||||
|
||||
@param vorbisFile The `VorbisFile` object containing the audio data.
|
||||
@return An `AudioBuffer` instance with the decoded audio data.
|
||||
**/
|
||||
#if lime_vorbis
|
||||
|
||||
public static function fromVorbisFile(vorbisFile:VorbisFile):AudioBuffer
|
||||
{
|
||||
if (vorbisFile == null) return null;
|
||||
|
||||
var info = vorbisFile.info();
|
||||
|
||||
var audioBuffer = new AudioBuffer();
|
||||
audioBuffer.channels = info.channels;
|
||||
audioBuffer.sampleRate = info.rate;
|
||||
audioBuffer.bitsPerSample = 16;
|
||||
|
||||
final pcmTotal = vorbisFile.pcmTotal(-1);
|
||||
if (!vorbisFile.seekable() || pcmTotal < (audioBuffer.sampleRate << 2)) {
|
||||
vorbisFile.rawSeek(0);
|
||||
|
||||
final isBigEndian = lime.system.System.endianness == lime.system.Endian.BIG_ENDIAN;
|
||||
final bytes = Bytes.alloc(Std.int((pcmTotal.high * 4294967296. + (pcmTotal.low >> 0)) * info.channels * (audioBuffer.bitsPerSample >> 3)));
|
||||
var total = 0, result = 0;
|
||||
do {
|
||||
result = vorbisFile.read(bytes, total, 0x1000, isBigEndian, 2, true);
|
||||
total += result;
|
||||
} while (result > 0 || result == Vorbis.HOLE);
|
||||
|
||||
audioBuffer.data = new UInt8Array(bytes);
|
||||
vorbisFile.clear();
|
||||
}
|
||||
else
|
||||
audioBuffer.__srcVorbisFile = vorbisFile;
|
||||
|
||||
return audioBuffer;
|
||||
}
|
||||
#else
|
||||
public static function fromVorbisFile(vorbisFile:Dynamic):AudioBuffer
|
||||
{
|
||||
return null;
|
||||
}
|
||||
#end
|
||||
|
||||
/**
|
||||
Asynchronously loads an `AudioBuffer` from a file.
|
||||
|
||||
@param path The file path to the audio data.
|
||||
@return A `Future` that resolves to the loaded `AudioBuffer`.
|
||||
**/
|
||||
public static function loadFromFile(path:String):Future<AudioBuffer>
|
||||
{
|
||||
#if (flash || (js && html5))
|
||||
var promise = new Promise<AudioBuffer>();
|
||||
|
||||
var audioBuffer = AudioBuffer.fromFile(path);
|
||||
|
||||
if (audioBuffer != null)
|
||||
{
|
||||
#if flash
|
||||
audioBuffer.__srcSound.addEventListener(flash.events.Event.COMPLETE, function(event)
|
||||
{
|
||||
promise.complete(audioBuffer);
|
||||
});
|
||||
|
||||
audioBuffer.__srcSound.addEventListener(flash.events.ProgressEvent.PROGRESS, function(event)
|
||||
{
|
||||
promise.progress(Std.int(event.bytesLoaded), Std.int(event.bytesTotal));
|
||||
});
|
||||
|
||||
audioBuffer.__srcSound.addEventListener(flash.events.IOErrorEvent.IO_ERROR, promise.error);
|
||||
#elseif (js && html5 && lime_howlerjs)
|
||||
if (audioBuffer != null)
|
||||
{
|
||||
audioBuffer.__srcHowl.on("load", function()
|
||||
{
|
||||
promise.complete(audioBuffer);
|
||||
});
|
||||
|
||||
audioBuffer.__srcHowl.on("loaderror", function(id, msg)
|
||||
{
|
||||
promise.error(msg);
|
||||
});
|
||||
|
||||
audioBuffer.__srcHowl.load();
|
||||
}
|
||||
#else
|
||||
promise.complete(audioBuffer);
|
||||
#end
|
||||
}
|
||||
else
|
||||
{
|
||||
promise.error(null);
|
||||
}
|
||||
|
||||
return promise.future;
|
||||
#else
|
||||
// TODO: Streaming
|
||||
|
||||
var request = new HTTPRequest<AudioBuffer>();
|
||||
return request.load(path).then(function(buffer)
|
||||
{
|
||||
if (buffer != null)
|
||||
{
|
||||
return Future.withValue(buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
return cast Future.withError("");
|
||||
}
|
||||
});
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
Asynchronously loads an `AudioBuffer` from multiple files.
|
||||
|
||||
@param paths An array of file paths to search for audio data.
|
||||
@return A `Future` that resolves to the loaded `AudioBuffer`.
|
||||
**/
|
||||
public static function loadFromFiles(paths:Array<String>):Future<AudioBuffer>
|
||||
{
|
||||
#if (js && html5 && lime_howlerjs)
|
||||
var promise = new Promise<AudioBuffer>();
|
||||
|
||||
var audioBuffer = AudioBuffer.fromFiles(paths);
|
||||
|
||||
if (audioBuffer != null)
|
||||
{
|
||||
audioBuffer.__srcHowl.on("load", function()
|
||||
{
|
||||
promise.complete(audioBuffer);
|
||||
});
|
||||
|
||||
audioBuffer.__srcHowl.on("loaderror", function()
|
||||
{
|
||||
promise.error(null);
|
||||
});
|
||||
|
||||
audioBuffer.__srcHowl.load();
|
||||
}
|
||||
else
|
||||
{
|
||||
promise.error(null);
|
||||
}
|
||||
|
||||
return promise.future;
|
||||
#else
|
||||
return new Future(fromFiles.bind(paths), true);
|
||||
#end
|
||||
}
|
||||
|
||||
private static function __getCodec(bytes:Bytes):String
|
||||
{
|
||||
var signature = bytes.getString(0, 4);
|
||||
|
||||
switch (signature)
|
||||
{
|
||||
case "OggS":
|
||||
return "audio/ogg";
|
||||
case "fLaC":
|
||||
return "audio/flac";
|
||||
case "RIFF" if (bytes.getString(8, 4) == "WAVE"):
|
||||
return "audio/wav";
|
||||
default:
|
||||
switch ([bytes.get(0), bytes.get(1), bytes.get(2)])
|
||||
{
|
||||
case [73, 68, 51] | [255, 251, _] | [255, 250, _] | [255, 243, _]: return "audio/mp3";
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
Log.error("Unsupported sound format");
|
||||
return null;
|
||||
}
|
||||
|
||||
// Get & Set Methods
|
||||
@:noCompletion private function get_src():Dynamic
|
||||
{
|
||||
#if (js && html5)
|
||||
#if lime_howlerjs
|
||||
return __srcHowl;
|
||||
#else
|
||||
return __srcAudio;
|
||||
#end
|
||||
#elseif flash
|
||||
return __srcSound;
|
||||
#elseif lime_vorbis
|
||||
return __srcVorbisFile;
|
||||
#else
|
||||
return __srcCustom;
|
||||
#end
|
||||
}
|
||||
|
||||
@:noCompletion private function set_src(value:Dynamic):Dynamic
|
||||
{
|
||||
#if (js && html5)
|
||||
#if lime_howlerjs
|
||||
return __srcHowl = value;
|
||||
#else
|
||||
return __srcAudio = value;
|
||||
#end
|
||||
#elseif flash
|
||||
return __srcSound = value;
|
||||
#elseif lime_vorbis
|
||||
return __srcVorbisFile = value;
|
||||
#else
|
||||
return __srcCustom = value;
|
||||
#end
|
||||
}
|
||||
}
|
||||
@@ -1,257 +0,0 @@
|
||||
package lime.media;
|
||||
|
||||
import lime.app.Event;
|
||||
import lime.math.Vector4;
|
||||
|
||||
#if !lime_debug
|
||||
@:fileXml('tags="haxe,release"')
|
||||
@:noDebug
|
||||
#end
|
||||
/**
|
||||
The `AudioSource` class provides a way to control audio playback in a Lime application.
|
||||
It allows for playing, pausing, and stopping audio, as well as controlling various
|
||||
audio properties such as gain, pitch, and looping.
|
||||
|
||||
Depending on the platform, the audio backend may vary, but the API remains consistent.
|
||||
|
||||
@see lime.media.AudioBuffer
|
||||
**/
|
||||
class AudioSource
|
||||
{
|
||||
private static var activeSources:Array<AudioSource> = [];
|
||||
|
||||
/**
|
||||
An event that is dispatched when the audio playback is complete.
|
||||
**/
|
||||
public var onComplete = new Event<Void->Void>();
|
||||
|
||||
/**
|
||||
An event that is dispatched when the audio playback looped.
|
||||
**/
|
||||
public var onLoop = new Event<Void->Void>();
|
||||
|
||||
/**
|
||||
The `AudioBuffer` associated with this `AudioSource`.
|
||||
**/
|
||||
public var buffer:AudioBuffer;
|
||||
|
||||
/**
|
||||
An property if this 'AudioSource' is playing.
|
||||
**/
|
||||
public var playing(get, null):Bool;
|
||||
|
||||
/**
|
||||
The current playback position of the audio, in milliseconds.
|
||||
**/
|
||||
public var currentTime(get, set):Float;
|
||||
|
||||
/**
|
||||
The gain (volume) of the audio. A value of `1.0` represents the default volume.
|
||||
**/
|
||||
public var gain(get, set):Float;
|
||||
|
||||
/**
|
||||
The length of the audio, in milliseconds.
|
||||
**/
|
||||
public var length(get, set):Null<Float>;
|
||||
|
||||
/**
|
||||
The number of times the audio will loop. A value of `0` means the audio will not loop.
|
||||
**/
|
||||
public var loops(get, set):Int;
|
||||
|
||||
/**
|
||||
In which audio playback time the audio will loop.
|
||||
**/
|
||||
public var loopTime(get, set):Float;
|
||||
|
||||
/**
|
||||
The pitch of the audio. A value of `1.0` represents the default pitch.
|
||||
**/
|
||||
public var pitch(get, set):Float;
|
||||
|
||||
/**
|
||||
The offset within the audio buffer to start playback, in samples.
|
||||
**/
|
||||
public var offset:Float;
|
||||
|
||||
/**
|
||||
The 3D position of the audio source, represented as a `Vector4`.
|
||||
**/
|
||||
public var position(get, set):Vector4;
|
||||
|
||||
/**
|
||||
The stereo pan of the audio source.
|
||||
**/
|
||||
public var pan(get, set):Float;
|
||||
|
||||
/**
|
||||
The latency of the audio source.
|
||||
**/
|
||||
public var latency(get, never):Float;
|
||||
|
||||
@:noCompletion private var __backend:AudioSourceBackend;
|
||||
|
||||
/**
|
||||
Creates a new `AudioSource` instance.
|
||||
@param buffer The `AudioBuffer` to associate with this `AudioSource`.
|
||||
@param offset The starting offset within the audio buffer, in samples.
|
||||
@param length The length of the audio to play, in milliseconds. If `null`, the full buffer is used.
|
||||
@param loops The number of times to loop the audio. `0` means no looping.
|
||||
**/
|
||||
public function new(buffer:AudioBuffer = null, offset:Float = 0, length:Null<Float> = null, loops:Int = 0)
|
||||
{
|
||||
this.buffer = buffer;
|
||||
this.offset = offset;
|
||||
|
||||
__backend = new AudioSourceBackend(this);
|
||||
|
||||
if (length != null && length != 0)
|
||||
{
|
||||
this.length = length;
|
||||
}
|
||||
|
||||
if (buffer != null)
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
||||
this.loops = loops;
|
||||
}
|
||||
|
||||
/**
|
||||
Releases any resources used by this `AudioSource`.
|
||||
**/
|
||||
inline public function dispose():Void
|
||||
{
|
||||
__backend.dispose();
|
||||
activeSources.remove(this);
|
||||
}
|
||||
|
||||
@:noCompletion inline private function init():Void
|
||||
{
|
||||
__backend.init();
|
||||
if (!activeSources.contains(this)) activeSources.push(this);
|
||||
}
|
||||
|
||||
/**
|
||||
Starts or resumes audio playback.
|
||||
**/
|
||||
inline public function play():Void
|
||||
{
|
||||
__backend.play();
|
||||
}
|
||||
|
||||
/**
|
||||
Pauses audio playback.
|
||||
**/
|
||||
inline public function pause():Void
|
||||
{
|
||||
__backend.pause();
|
||||
}
|
||||
|
||||
/**
|
||||
Stops audio playback and resets the playback position to the beginning.
|
||||
**/
|
||||
inline public function stop():Void
|
||||
{
|
||||
__backend.stop();
|
||||
}
|
||||
|
||||
// Get & Set Methods
|
||||
@:noCompletion inline private function get_playing():Bool
|
||||
{
|
||||
@:privateAccess return __backend.playing;
|
||||
}
|
||||
|
||||
@:noCompletion inline private function get_currentTime():Float
|
||||
{
|
||||
return __backend.getCurrentTime();
|
||||
}
|
||||
|
||||
@:noCompletion inline private function set_currentTime(value:Float):Float
|
||||
{
|
||||
return __backend.setCurrentTime(value);
|
||||
}
|
||||
|
||||
@:noCompletion inline private function get_gain():Float
|
||||
{
|
||||
return __backend.getGain();
|
||||
}
|
||||
|
||||
@:noCompletion inline private function set_gain(value:Float):Float
|
||||
{
|
||||
return __backend.setGain(value);
|
||||
}
|
||||
|
||||
@:noCompletion inline private function get_length():Null<Float>
|
||||
{
|
||||
return __backend.getLength();
|
||||
}
|
||||
|
||||
@:noCompletion inline private function set_length(value:Null<Float>):Null<Float>
|
||||
{
|
||||
return __backend.setLength(value);
|
||||
}
|
||||
|
||||
@:noCompletion inline private function get_loops():Int
|
||||
{
|
||||
return __backend.getLoops();
|
||||
}
|
||||
|
||||
@:noCompletion inline private function set_loops(value:Int):Int
|
||||
{
|
||||
return __backend.setLoops(value);
|
||||
}
|
||||
|
||||
@:noCompletion inline private function get_loopTime():Float
|
||||
{
|
||||
return __backend.getLoopTime();
|
||||
}
|
||||
|
||||
@:noCompletion inline private function set_loopTime(value:Float):Float
|
||||
{
|
||||
return __backend.setLoopTime(value);
|
||||
}
|
||||
|
||||
@:noCompletion inline private function get_pitch():Float
|
||||
{
|
||||
return __backend.getPitch();
|
||||
}
|
||||
|
||||
@:noCompletion inline private function set_pitch(value:Float):Float
|
||||
{
|
||||
return __backend.setPitch(value);
|
||||
}
|
||||
|
||||
@:noCompletion inline private function get_position():Vector4
|
||||
{
|
||||
return __backend.getPosition();
|
||||
}
|
||||
|
||||
@:noCompletion inline private function set_position(value:Vector4):Vector4
|
||||
{
|
||||
return __backend.setPosition(value);
|
||||
}
|
||||
|
||||
@:noCompletion inline private function get_pan():Float
|
||||
{
|
||||
return __backend.getPan();
|
||||
}
|
||||
|
||||
@:noCompletion inline private function set_pan(value:Float):Float
|
||||
{
|
||||
return __backend.setPan(value);
|
||||
}
|
||||
|
||||
@:noCompletion inline private function get_latency():Float
|
||||
{
|
||||
return __backend.getLatency();
|
||||
}
|
||||
}
|
||||
|
||||
#if (js && html5)
|
||||
@:noCompletion private typedef AudioSourceBackend = lime._internal.backend.html5.HTML5AudioSource;
|
||||
#else
|
||||
@:noCompletion private typedef AudioSourceBackend = lime._internal.backend.native.NativeAudioSource;
|
||||
#end
|
||||
@@ -1,356 +0,0 @@
|
||||
package lime.media.vorbis;
|
||||
|
||||
#if (!lime_doc_gen || lime_vorbis)
|
||||
import haxe.Int64;
|
||||
import haxe.io.Bytes;
|
||||
import lime._internal.backend.native.NativeCFFI;
|
||||
|
||||
#if hl
|
||||
@:keep
|
||||
#end
|
||||
@:access(lime._internal.backend.native.NativeCFFI)
|
||||
class VorbisFile
|
||||
{
|
||||
public var bitstream(default, null):Int;
|
||||
|
||||
@:noCompletion private var handle:Dynamic;
|
||||
|
||||
@:noCompletion var _filePath:String;
|
||||
@:noCompletion var _bytes:Bytes;
|
||||
|
||||
@:noCompletion private function new(handle:Dynamic)
|
||||
{
|
||||
this.handle = handle;
|
||||
}
|
||||
|
||||
public function bitrate(bitstream:Int = -1):Int
|
||||
{
|
||||
#if (lime_cffi && lime_vorbis && !macro)
|
||||
return NativeCFFI.lime_vorbis_file_bitrate(handle, bitstream);
|
||||
#else
|
||||
return 0;
|
||||
#end
|
||||
}
|
||||
|
||||
public function bitrateInstant():Int
|
||||
{
|
||||
#if (lime_cffi && lime_vorbis && !macro)
|
||||
return NativeCFFI.lime_vorbis_file_bitrate_instant(handle);
|
||||
#else
|
||||
return 0;
|
||||
#end
|
||||
}
|
||||
|
||||
public function clear():Void
|
||||
{
|
||||
#if (lime_cffi && lime_vorbis && !macro)
|
||||
NativeCFFI.lime_vorbis_file_clear(handle);
|
||||
#end
|
||||
handle = null;
|
||||
_filePath = null;
|
||||
_bytes = null;
|
||||
}
|
||||
|
||||
public function clone():VorbisFile
|
||||
{
|
||||
if (_filePath != null) return fromFile(_filePath);
|
||||
if (_bytes != null) return fromBytes(_bytes);
|
||||
return null;
|
||||
}
|
||||
|
||||
public function comment(bitstream:Int = -1):VorbisComment
|
||||
{
|
||||
#if (lime_cffi && lime_vorbis && !macro)
|
||||
var data = NativeCFFI.lime_vorbis_file_comment(handle, bitstream);
|
||||
|
||||
if (data != null)
|
||||
{
|
||||
var comment = new VorbisComment();
|
||||
comment.userComments = data.userComments;
|
||||
comment.vendor = data.vendor;
|
||||
return comment;
|
||||
}
|
||||
#end
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function crosslap(other:VorbisFile):Int
|
||||
{
|
||||
#if (lime_cffi && lime_vorbis && !macro)
|
||||
return NativeCFFI.lime_vorbis_file_crosslap(handle, other.handle);
|
||||
#else
|
||||
return 0;
|
||||
#end
|
||||
}
|
||||
|
||||
public static function fromBytes(bytes:Bytes):VorbisFile
|
||||
{
|
||||
#if (lime_cffi && lime_vorbis && !macro)
|
||||
var handle = NativeCFFI.lime_vorbis_file_from_bytes(bytes);
|
||||
|
||||
if (handle != null)
|
||||
{
|
||||
var vorbisFile = new VorbisFile(handle);
|
||||
vorbisFile._bytes = bytes;
|
||||
return vorbisFile;
|
||||
}
|
||||
#end
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function fromFile(path:String):VorbisFile
|
||||
{
|
||||
#if (lime_cffi && lime_vorbis && !macro)
|
||||
var handle = NativeCFFI.lime_vorbis_file_from_file(path);
|
||||
|
||||
if (handle != null)
|
||||
{
|
||||
var vorbisFile = new VorbisFile(handle);
|
||||
vorbisFile._filePath = path;
|
||||
return vorbisFile;
|
||||
}
|
||||
#end
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function info(bitstream:Int = -1):VorbisInfo
|
||||
{
|
||||
#if (lime_cffi && lime_vorbis && !macro)
|
||||
var data = NativeCFFI.lime_vorbis_file_info(handle, bitstream);
|
||||
|
||||
if (data != null)
|
||||
{
|
||||
var info = new VorbisInfo();
|
||||
info.bitrateLower = data.bitrateLower;
|
||||
info.bitrateNominal = data.bitrateNominal;
|
||||
info.bitrateUpper = data.bitrateUpper;
|
||||
info.channels = data.channels;
|
||||
info.rate = data.rate;
|
||||
info.version = data.version;
|
||||
return info;
|
||||
}
|
||||
#end
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function pcmSeek(pos:Int64):Int
|
||||
{
|
||||
#if (lime_cffi && lime_vorbis && !macro)
|
||||
return NativeCFFI.lime_vorbis_file_pcm_seek(handle, pos.low, pos.high);
|
||||
#else
|
||||
return 0;
|
||||
#end
|
||||
}
|
||||
|
||||
public function pcmSeekLap(pos:Int64):Int
|
||||
{
|
||||
#if (lime_cffi && lime_vorbis && !macro)
|
||||
return NativeCFFI.lime_vorbis_file_pcm_seek_lap(handle, pos.low, pos.high);
|
||||
#else
|
||||
return 0;
|
||||
#end
|
||||
}
|
||||
|
||||
public function pcmSeekPage(pos:Int64):Int
|
||||
{
|
||||
#if (lime_cffi && lime_vorbis && !macro)
|
||||
return NativeCFFI.lime_vorbis_file_pcm_seek_page(handle, pos.low, pos.high);
|
||||
#else
|
||||
return 0;
|
||||
#end
|
||||
}
|
||||
|
||||
public function pcmSeekPageLap(pos:Int64):Int
|
||||
{
|
||||
#if (lime_cffi && lime_vorbis && !macro)
|
||||
return NativeCFFI.lime_vorbis_file_pcm_seek_page_lap(handle, pos.low, pos.high);
|
||||
#else
|
||||
return 0;
|
||||
#end
|
||||
}
|
||||
|
||||
public function pcmTell():Int64
|
||||
{
|
||||
#if (lime_cffi && lime_vorbis && !macro)
|
||||
var data = NativeCFFI.lime_vorbis_file_pcm_tell(handle);
|
||||
|
||||
if (data != null)
|
||||
{
|
||||
return Int64.make(data.high, data.low);
|
||||
}
|
||||
#end
|
||||
|
||||
return Int64.ofInt(0);
|
||||
}
|
||||
|
||||
public function pcmTotal(bitstream:Int = -1):Int64
|
||||
{
|
||||
#if (lime_cffi && lime_vorbis && !macro)
|
||||
var data = NativeCFFI.lime_vorbis_file_pcm_total(handle, bitstream);
|
||||
|
||||
if (data != null)
|
||||
{
|
||||
return Int64.make(data.high, data.low);
|
||||
}
|
||||
#end
|
||||
|
||||
return Int64.ofInt(0);
|
||||
}
|
||||
|
||||
public function rawSeek(pos:Int64):Int
|
||||
{
|
||||
#if (lime_cffi && lime_vorbis && !macro)
|
||||
return NativeCFFI.lime_vorbis_file_raw_seek(handle, pos.low, pos.high);
|
||||
#else
|
||||
return 0;
|
||||
#end
|
||||
}
|
||||
|
||||
public function rawSeekLap(pos:Int64):Int
|
||||
{
|
||||
#if (lime_cffi && lime_vorbis && !macro)
|
||||
return NativeCFFI.lime_vorbis_file_raw_seek_lap(handle, pos.low, pos.high);
|
||||
#else
|
||||
return 0;
|
||||
#end
|
||||
}
|
||||
|
||||
public function rawTell():Int64
|
||||
{
|
||||
#if (lime_cffi && lime_vorbis && !macro)
|
||||
var data = NativeCFFI.lime_vorbis_file_raw_tell(handle);
|
||||
|
||||
if (data != null)
|
||||
{
|
||||
return Int64.make(data.high, data.low);
|
||||
}
|
||||
#end
|
||||
|
||||
return Int64.ofInt(0);
|
||||
}
|
||||
|
||||
public function rawTotal(bitstream:Int = -1):Int64
|
||||
{
|
||||
#if (lime_cffi && lime_vorbis && !macro)
|
||||
var data = NativeCFFI.lime_vorbis_file_raw_total(handle, bitstream);
|
||||
|
||||
if (data != null)
|
||||
{
|
||||
return Int64.make(data.high, data.low);
|
||||
}
|
||||
#end
|
||||
|
||||
return Int64.ofInt(0);
|
||||
}
|
||||
|
||||
public function read(buffer:Bytes, position:Int, length:Int = 4096, bigEndianPacking:Bool = false, wordSize:Int = 2, signed:Bool = true):Int
|
||||
{
|
||||
#if (lime_cffi && lime_vorbis && !macro)
|
||||
var data = NativeCFFI.lime_vorbis_file_read(handle, buffer, position, length, bigEndianPacking, wordSize, signed);
|
||||
if (data == null) return 0;
|
||||
bitstream = data.bitstream;
|
||||
return data.returnValue;
|
||||
#else
|
||||
return 0;
|
||||
#end
|
||||
}
|
||||
|
||||
// public function readFilter (buffer:Bytes, length:Int = 4096, endianness:Endian = LITTLE_ENDIAN, wordSize:Int = 2, signed:Bool = true, bitstream:Int = 0, filter, filter_param
|
||||
public function readFloat(pcmChannels:Bytes, samples:Int):Int
|
||||
{
|
||||
#if (lime_cffi && lime_vorbis && !macro)
|
||||
var data = NativeCFFI.lime_vorbis_file_read_float(handle, pcmChannels, samples);
|
||||
if (data == null) return 0;
|
||||
bitstream = data.bitstream;
|
||||
return data.returnValue;
|
||||
#else
|
||||
return 0;
|
||||
#end
|
||||
}
|
||||
|
||||
public function seekable():Bool
|
||||
{
|
||||
#if (lime_cffi && lime_vorbis && !macro)
|
||||
return NativeCFFI.lime_vorbis_file_seekable(handle);
|
||||
#else
|
||||
return false;
|
||||
#end
|
||||
}
|
||||
|
||||
public function serialNumber(bitstream:Int = -1):Int
|
||||
{
|
||||
#if (lime_cffi && lime_vorbis && !macro)
|
||||
return NativeCFFI.lime_vorbis_file_serial_number(handle, bitstream);
|
||||
#else
|
||||
return 0;
|
||||
#end
|
||||
}
|
||||
|
||||
public function streams():Int
|
||||
{
|
||||
#if (lime_cffi && lime_vorbis && !macro)
|
||||
return NativeCFFI.lime_vorbis_file_streams(handle);
|
||||
#else
|
||||
return 0;
|
||||
#end
|
||||
}
|
||||
|
||||
public function timeSeek(s:Float):Int
|
||||
{
|
||||
#if (lime_cffi && lime_vorbis && !macro)
|
||||
return NativeCFFI.lime_vorbis_file_time_seek(handle, s);
|
||||
#else
|
||||
return 0;
|
||||
#end
|
||||
}
|
||||
|
||||
public function timeSeekLap(s:Float):Int
|
||||
{
|
||||
#if (lime_cffi && lime_vorbis && !macro)
|
||||
return NativeCFFI.lime_vorbis_file_time_seek_lap(handle, s);
|
||||
#else
|
||||
return 0;
|
||||
#end
|
||||
}
|
||||
|
||||
public function timeSeekPage(s:Float):Int
|
||||
{
|
||||
#if (lime_cffi && lime_vorbis && !macro)
|
||||
return NativeCFFI.lime_vorbis_file_time_seek_page(handle, s);
|
||||
#else
|
||||
return 0;
|
||||
#end
|
||||
}
|
||||
|
||||
public function timeSeekPageLap(s:Float):Int
|
||||
{
|
||||
#if (lime_cffi && lime_vorbis && !macro)
|
||||
return NativeCFFI.lime_vorbis_file_time_seek_page_lap(handle, s);
|
||||
#else
|
||||
return 0;
|
||||
#end
|
||||
}
|
||||
|
||||
public function timeTell():Float
|
||||
{
|
||||
#if (lime_cffi && lime_vorbis && !macro)
|
||||
return NativeCFFI.lime_vorbis_file_time_tell(handle);
|
||||
#else
|
||||
return 0;
|
||||
#end
|
||||
}
|
||||
|
||||
public function timeTotal(bitstream:Int = -1):Float
|
||||
{
|
||||
#if (lime_cffi && lime_vorbis && !macro)
|
||||
return NativeCFFI.lime_vorbis_file_time_total(handle, bitstream);
|
||||
#else
|
||||
return 0;
|
||||
#end
|
||||
}
|
||||
}
|
||||
#end
|
||||
@@ -1,905 +0,0 @@
|
||||
package lime.system;
|
||||
|
||||
import haxe.Constraints;
|
||||
import lime._internal.backend.native.NativeCFFI;
|
||||
import lime.app.Application;
|
||||
import lime.graphics.RenderContextAttributes;
|
||||
import lime.math.Rectangle;
|
||||
import lime.ui.WindowAttributes;
|
||||
import lime.utils.ArrayBuffer;
|
||||
import lime.utils.UInt8Array;
|
||||
import lime.utils.UInt16Array;
|
||||
#if flash
|
||||
import openfl.net.URLRequest;
|
||||
import openfl.system.Capabilities;
|
||||
import openfl.Lib;
|
||||
#end
|
||||
#if air
|
||||
import openfl.desktop.NativeApplication;
|
||||
#end
|
||||
#if ((js && html5) || electron)
|
||||
import js.html.Element;
|
||||
import js.Browser;
|
||||
#end
|
||||
#if sys
|
||||
import sys.io.Process;
|
||||
#end
|
||||
|
||||
/**
|
||||
Access operating system level settings and operations.
|
||||
**/
|
||||
#if !lime_debug
|
||||
@:fileXml('tags="haxe,release"')
|
||||
@:noDebug
|
||||
#end
|
||||
@:access(lime._internal.backend.native.NativeCFFI)
|
||||
@:access(lime.system.Display)
|
||||
@:access(lime.system.DisplayMode)
|
||||
#if (cpp && windows && !HXCPP_MINGW && !lime_disable_gpu_hint)
|
||||
@:cppFileCode('
|
||||
#if defined(HX_WINDOWS)
|
||||
extern "C" {
|
||||
_declspec(dllexport) unsigned long NvOptimusEnablement = 0x00000001;
|
||||
_declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
|
||||
}
|
||||
#endif
|
||||
')
|
||||
#end
|
||||
class System
|
||||
{
|
||||
/**
|
||||
Determines if the screen saver is allowed to start or not.
|
||||
**/
|
||||
public static var allowScreenTimeout(get, set):Bool;
|
||||
|
||||
/**
|
||||
The path to the directory where the application is installed, along with
|
||||
its supporting files. In many cases, this directory is read-only, and
|
||||
attempts to write to files, create new files, or delete files in this
|
||||
directory are likely fail.
|
||||
**/
|
||||
public static var applicationDirectory(get, never):String;
|
||||
|
||||
/**
|
||||
The application's dedicated storage directory, which unique to each
|
||||
application and user. Useful for storing settings on a user-specific
|
||||
and application-specific basis.
|
||||
|
||||
This directory may or may not be removed when the application is
|
||||
uninstalled, and it depends on the platform and installer technology
|
||||
that is used.
|
||||
**/
|
||||
public static var applicationStorageDirectory(get, never):String;
|
||||
|
||||
/**
|
||||
The path to the directory containing the user's desktop.
|
||||
**/
|
||||
public static var desktopDirectory(get, never):String;
|
||||
|
||||
public static var deviceModel(get, never):String;
|
||||
public static var deviceVendor(get, never):String;
|
||||
public static var disableCFFI:Bool;
|
||||
|
||||
/**
|
||||
The path to the directory containing the user's documents.
|
||||
**/
|
||||
public static var documentsDirectory(get, never):String;
|
||||
|
||||
/**
|
||||
The platform's default endianness for bytes.
|
||||
**/
|
||||
public static var endianness(get, never):Endian;
|
||||
|
||||
/**
|
||||
The path to the directory where fonts are installed.
|
||||
**/
|
||||
public static var fontsDirectory(get, never):String;
|
||||
|
||||
/**
|
||||
The number of available video displays.
|
||||
**/
|
||||
public static var numDisplays(get, never):Int;
|
||||
|
||||
public static var platformLabel(get, never):String;
|
||||
public static var platformName(get, never):String;
|
||||
public static var platformVersion(get, never):String;
|
||||
|
||||
/**
|
||||
The path to the user's home directory.
|
||||
**/
|
||||
public static var userDirectory(get, never):String;
|
||||
|
||||
@:noCompletion private static var __applicationDirectory:String;
|
||||
@:noCompletion private static var __applicationEntryPoint:Map<String, Function>;
|
||||
@:noCompletion private static var __applicationStorageDirectory:String;
|
||||
@:noCompletion private static var __desktopDirectory:String;
|
||||
@:noCompletion private static var __deviceModel:String;
|
||||
@:noCompletion private static var __deviceVendor:String;
|
||||
@:noCompletion private static var __directories = new Map<SystemDirectory, String>();
|
||||
@:noCompletion private static var __documentsDirectory:String;
|
||||
@:noCompletion private static var __endianness:Endian;
|
||||
@:noCompletion private static var __fontsDirectory:String;
|
||||
@:noCompletion private static var __platformLabel:String;
|
||||
@:noCompletion private static var __platformName:String;
|
||||
@:noCompletion private static var __platformVersion:String;
|
||||
@:noCompletion private static var __userDirectory:String;
|
||||
|
||||
#if (js && html5)
|
||||
@:keep @:expose("lime.embed")
|
||||
public static function embed(projectName:String, element:Dynamic, width:Null<Int> = null, height:Null<Int> = null, config:Dynamic = null):Void
|
||||
{
|
||||
if (__applicationEntryPoint == null) return;
|
||||
|
||||
if (__applicationEntryPoint.exists(projectName))
|
||||
{
|
||||
var htmlElement:Element = null;
|
||||
|
||||
if ((element is String))
|
||||
{
|
||||
htmlElement = cast Browser.document.getElementById(element);
|
||||
}
|
||||
else if (element == null)
|
||||
{
|
||||
htmlElement = cast Browser.document.createElement("div");
|
||||
}
|
||||
else
|
||||
{
|
||||
htmlElement = cast element;
|
||||
}
|
||||
|
||||
if (htmlElement == null)
|
||||
{
|
||||
Browser.window.console.log("[lime.embed] ERROR: Cannot find target element: " + element);
|
||||
return;
|
||||
}
|
||||
|
||||
if (width == null)
|
||||
{
|
||||
width = 0;
|
||||
}
|
||||
|
||||
if (height == null)
|
||||
{
|
||||
height = 0;
|
||||
}
|
||||
|
||||
if (config == null) config = {};
|
||||
|
||||
if (Reflect.hasField(config, "background") && (config.background is String))
|
||||
{
|
||||
var background = StringTools.replace(Std.string(config.background), "#", "");
|
||||
|
||||
if (background.indexOf("0x") > -1)
|
||||
{
|
||||
config.background = Std.parseInt(background);
|
||||
}
|
||||
else
|
||||
{
|
||||
config.background = Std.parseInt("0x" + background);
|
||||
}
|
||||
}
|
||||
|
||||
config.element = htmlElement;
|
||||
config.width = width;
|
||||
config.height = height;
|
||||
|
||||
__applicationEntryPoint[projectName](config);
|
||||
}
|
||||
}
|
||||
#end
|
||||
|
||||
#if (!lime_doc_gen || sys)
|
||||
/**
|
||||
Attempts to exit the application. Dispatches `onExit`, and will not
|
||||
exit if the event is canceled.
|
||||
**/
|
||||
public static function exit(code:Int):Void
|
||||
{
|
||||
var currentApp = Application.current;
|
||||
#if ((sys || (js && html5) || air) && !macro)
|
||||
if (currentApp != null)
|
||||
{
|
||||
currentApp.onExit.dispatch(code);
|
||||
|
||||
if (currentApp.onExit.canceled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
#end
|
||||
|
||||
#if sys
|
||||
Sys.exit(code);
|
||||
#elseif (js && html5)
|
||||
if (currentApp != null && currentApp.window != null)
|
||||
{
|
||||
currentApp.window.close();
|
||||
}
|
||||
#elseif air
|
||||
NativeApplication.nativeApplication.exit(code);
|
||||
#end
|
||||
}
|
||||
#end
|
||||
|
||||
/**
|
||||
Returns information about the video display with the specified ID.
|
||||
**/
|
||||
public static function getDisplay(id:Int):Display
|
||||
{
|
||||
#if (lime_cffi && !macro)
|
||||
var displayInfo:Dynamic = NativeCFFI.lime_system_get_display(id);
|
||||
|
||||
if (displayInfo != null)
|
||||
{
|
||||
var display = new Display();
|
||||
display.id = id;
|
||||
#if hl
|
||||
display.name = @:privateAccess String.fromUTF8(displayInfo.name);
|
||||
#else
|
||||
display.name = displayInfo.name;
|
||||
#end
|
||||
display.bounds = new Rectangle(displayInfo.bounds.x, displayInfo.bounds.y, displayInfo.bounds.width, displayInfo.bounds.height);
|
||||
|
||||
#if ios
|
||||
var tablet = NativeCFFI.lime_system_get_ios_tablet();
|
||||
var scale = Application.current.window.scale;
|
||||
if (!tablet && scale > 2.46)
|
||||
{
|
||||
display.dpi = 401; // workaround for iPhone Plus
|
||||
}
|
||||
else
|
||||
{
|
||||
display.dpi = (tablet ? 132 : 163) * scale;
|
||||
}
|
||||
#elseif android
|
||||
var getDisplayDPI = JNI.createStaticMethod("org/haxe/lime/GameActivity", "getDisplayXDPI", "()D");
|
||||
display.dpi = Math.round(getDisplayDPI());
|
||||
#else
|
||||
display.dpi = displayInfo.dpi;
|
||||
#end
|
||||
|
||||
display.supportedModes = [];
|
||||
|
||||
var displayMode;
|
||||
|
||||
#if hl
|
||||
var supportedModes:hl.NativeArray<Dynamic> = displayInfo.supportedModes;
|
||||
#else
|
||||
var supportedModes:Array<Dynamic> = displayInfo.supportedModes;
|
||||
#end
|
||||
for (mode in supportedModes)
|
||||
{
|
||||
displayMode = new DisplayMode(mode.width, mode.height, mode.refreshRate, mode.pixelFormat);
|
||||
display.supportedModes.push(displayMode);
|
||||
}
|
||||
|
||||
var mode = displayInfo.currentMode;
|
||||
var currentMode = new DisplayMode(mode.width, mode.height, mode.refreshRate, mode.pixelFormat);
|
||||
|
||||
for (mode in display.supportedModes)
|
||||
{
|
||||
if (currentMode.pixelFormat == mode.pixelFormat
|
||||
&& currentMode.width == mode.width
|
||||
&& currentMode.height == mode.height
|
||||
&& currentMode.refreshRate == mode.refreshRate)
|
||||
{
|
||||
currentMode = mode;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
display.currentMode = currentMode;
|
||||
|
||||
return display;
|
||||
}
|
||||
#elseif (flash || html5)
|
||||
if (id == 0)
|
||||
{
|
||||
var display = new Display();
|
||||
display.id = 0;
|
||||
display.name = "Generic Display";
|
||||
|
||||
#if flash
|
||||
display.dpi = Capabilities.screenDPI;
|
||||
display.currentMode = new DisplayMode(Std.int(Capabilities.screenResolutionX), Std.int(Capabilities.screenResolutionY), 60, ARGB32);
|
||||
#elseif (js && html5)
|
||||
// var div = Browser.document.createElement ("div");
|
||||
// div.style.width = "1in";
|
||||
// Browser.document.body.appendChild (div);
|
||||
// var ppi = Browser.document.defaultView.getComputedStyle (div, null).getPropertyValue ("width");
|
||||
// Browser.document.body.removeChild (div);
|
||||
// display.dpi = Std.parseFloat (ppi);
|
||||
display.dpi = 96 * Browser.window.devicePixelRatio;
|
||||
display.currentMode = new DisplayMode(Browser.window.screen.width, Browser.window.screen.height, 60, ARGB32);
|
||||
#end
|
||||
|
||||
display.supportedModes = [display.currentMode];
|
||||
display.bounds = new Rectangle(0, 0, display.currentMode.width, display.currentMode.height);
|
||||
return display;
|
||||
}
|
||||
#end
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
The number of milliseconds since the application was initialized.
|
||||
**/
|
||||
public static function getTimer():Int
|
||||
{
|
||||
#if flash
|
||||
return flash.Lib.getTimer();
|
||||
#elseif ((js && !nodejs) || electron)
|
||||
return Std.int(Browser.window.performance.now());
|
||||
#elseif (lime_cffi && !macro)
|
||||
return cast NativeCFFI.lime_system_get_timer();
|
||||
#elseif cpp
|
||||
return Std.int(untyped __global__.__time_stamp() * 1000);
|
||||
#elseif sys
|
||||
return Std.int(Sys.time() * 1000);
|
||||
#else
|
||||
return 0;
|
||||
#end
|
||||
}
|
||||
|
||||
#if (!lime_doc_gen || lime_cffi)
|
||||
public static inline function load(library:String, method:String, args:Int = 0, lazy:Bool = false):Dynamic
|
||||
{
|
||||
#if !macro
|
||||
return CFFI.load(library, method, args, lazy);
|
||||
#else
|
||||
return null;
|
||||
#end
|
||||
}
|
||||
#end
|
||||
|
||||
/**
|
||||
Opens a file with the suste, default application.
|
||||
|
||||
In a web browser, opens a URL with target `_blank`.
|
||||
**/
|
||||
public static function openFile(path:String):Void
|
||||
{
|
||||
if (path != null)
|
||||
{
|
||||
#if (sys && windows)
|
||||
Sys.command("start", ["", path]);
|
||||
#elseif mac
|
||||
Sys.command("/usr/bin/open", [path]);
|
||||
#elseif linux
|
||||
// generally `xdg-open` should work in every distro
|
||||
var cmd = Sys.command("xdg-open", [path, "&"]);
|
||||
// run old command JUST IN CASE it fails, which it shouldn't
|
||||
if (cmd != 0) cmd = Sys.command("/usr/bin/xdg-open", [path, "&"]);
|
||||
#elseif (js && html5)
|
||||
Browser.window.open(path, "_blank");
|
||||
#elseif flash
|
||||
Lib.getURL(new URLRequest(path), "_blank");
|
||||
#elseif android
|
||||
var openFile = JNI.createStaticMethod("org/haxe/lime/GameActivity", "openFile", "(Ljava/lang/String;)V");
|
||||
openFile(path);
|
||||
#elseif (lime_cffi && !macro)
|
||||
NativeCFFI.lime_system_open_file(path);
|
||||
#end
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Opens a URL with the specified target web browser window.
|
||||
**/
|
||||
public static function openURL(url:String, target:String = "_blank"):Void
|
||||
{
|
||||
if (url != null)
|
||||
{
|
||||
#if desktop
|
||||
openFile(url);
|
||||
#elseif (js && html5)
|
||||
Browser.window.open(url, target);
|
||||
#elseif flash
|
||||
Lib.getURL(new URLRequest(url), target);
|
||||
#elseif android
|
||||
var openURL = JNI.createStaticMethod("org/haxe/lime/GameActivity", "openURL", "(Ljava/lang/String;Ljava/lang/String;)V");
|
||||
openURL(url, target);
|
||||
#elseif (lime_cffi && !macro)
|
||||
NativeCFFI.lime_system_open_url(url, target);
|
||||
#end
|
||||
}
|
||||
}
|
||||
|
||||
@:noCompletion private static function __copyMissingFields(target:Dynamic, source:Dynamic):Void
|
||||
{
|
||||
if (source == null || target == null) return;
|
||||
|
||||
for (field in Reflect.fields(source))
|
||||
{
|
||||
if (!Reflect.hasField(target, field))
|
||||
{
|
||||
Reflect.setField(target, field, Reflect.field(source, field));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@:noCompletion private static function __getDirectory(type:SystemDirectory):String
|
||||
{
|
||||
#if (lime_cffi && !macro)
|
||||
if (__directories.exists(type))
|
||||
{
|
||||
return __directories.get(type);
|
||||
}
|
||||
else
|
||||
{
|
||||
var path:String;
|
||||
|
||||
if (type == APPLICATION_STORAGE)
|
||||
{
|
||||
var company = "MyCompany";
|
||||
var file = "MyApplication";
|
||||
|
||||
if (Application.current != null)
|
||||
{
|
||||
if (Application.current.meta.exists("company"))
|
||||
{
|
||||
company = Application.current.meta.get("company");
|
||||
}
|
||||
|
||||
if (Application.current.meta.exists("file"))
|
||||
{
|
||||
file = Application.current.meta.get("file");
|
||||
}
|
||||
}
|
||||
|
||||
#if hl
|
||||
path = @:privateAccess String.fromUTF8(NativeCFFI.lime_system_get_directory(type, company, file));
|
||||
#else
|
||||
path = NativeCFFI.lime_system_get_directory(type, company, file);
|
||||
#end
|
||||
}
|
||||
else
|
||||
{
|
||||
#if hl
|
||||
path = @:privateAccess String.fromUTF8(NativeCFFI.lime_system_get_directory(type, null, null));
|
||||
#else
|
||||
path = NativeCFFI.lime_system_get_directory(type, null, null);
|
||||
#end
|
||||
}
|
||||
|
||||
#if windows
|
||||
var seperator = "\\";
|
||||
#else
|
||||
var seperator = "/";
|
||||
#end
|
||||
|
||||
if (path != null && path.length > 0 && !StringTools.endsWith(path, seperator))
|
||||
{
|
||||
path += seperator;
|
||||
}
|
||||
|
||||
__directories.set(type, path);
|
||||
return path;
|
||||
}
|
||||
#elseif flash
|
||||
if (type != FONTS && Capabilities.playerType == "Desktop")
|
||||
{
|
||||
var propertyName = switch (type)
|
||||
{
|
||||
case APPLICATION: "applicationDirectory";
|
||||
case APPLICATION_STORAGE: "applicationStorageDirectory";
|
||||
case DESKTOP: "desktopDirectory";
|
||||
case DOCUMENTS: "documentsDirectory";
|
||||
default: "userDirectory";
|
||||
}
|
||||
|
||||
return Reflect.getProperty(Type.resolveClass("flash.filesystem.File"), propertyName).nativePath;
|
||||
}
|
||||
#end
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
#if sys
|
||||
private static function __parseArguments(attributes:WindowAttributes):Void
|
||||
{
|
||||
// TODO: Handle default arguments, like --window-fps=60
|
||||
|
||||
var arguments = Sys.args();
|
||||
var stripQuotes = ~/^['"](.*)['"]$/;
|
||||
var equals, argValue, parameters = null;
|
||||
var windowParamPrefix = "--window-";
|
||||
|
||||
if (arguments != null)
|
||||
{
|
||||
for (argument in arguments)
|
||||
{
|
||||
equals = argument.indexOf("=");
|
||||
|
||||
if (equals > 0)
|
||||
{
|
||||
argValue = argument.substr(equals + 1);
|
||||
|
||||
if (stripQuotes.match(argValue))
|
||||
{
|
||||
argValue = stripQuotes.matched(1);
|
||||
}
|
||||
|
||||
if (parameters == null) parameters = new Map<String, String>();
|
||||
parameters.set(argument.substr(0, equals), argValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (parameters != null)
|
||||
{
|
||||
if (attributes.parameters == null) attributes.parameters = {};
|
||||
if (attributes.context == null) attributes.context = {};
|
||||
|
||||
for (parameter in parameters.keys())
|
||||
{
|
||||
argValue = parameters.get(parameter);
|
||||
|
||||
if (#if lime_disable_window_override false && #end StringTools.startsWith(parameter, windowParamPrefix))
|
||||
{
|
||||
switch (parameter.substr(windowParamPrefix.length))
|
||||
{
|
||||
case "allow-high-dpi":
|
||||
attributes.allowHighDPI = __parseBool(argValue);
|
||||
case "always-on-top":
|
||||
attributes.alwaysOnTop = __parseBool(argValue);
|
||||
case "antialiasing":
|
||||
attributes.context.antialiasing = Std.parseInt(argValue);
|
||||
case "background":
|
||||
attributes.context.background = (argValue == "" || argValue == "null") ? null : Std.parseInt(argValue);
|
||||
case "borderless":
|
||||
attributes.borderless = __parseBool(argValue);
|
||||
case "colorDepth":
|
||||
attributes.context.colorDepth = Std.parseInt(argValue);
|
||||
case "depth", "depth-buffer":
|
||||
attributes.context.depth = __parseBool(argValue);
|
||||
// case "display": windowConfig.display = Std.parseInt (argValue);
|
||||
case "fullscreen":
|
||||
attributes.fullscreen = __parseBool(argValue);
|
||||
case "hardware":
|
||||
attributes.context.hardware = __parseBool(argValue);
|
||||
case "height":
|
||||
attributes.height = Std.parseInt(argValue);
|
||||
case "hidden":
|
||||
attributes.hidden = __parseBool(argValue);
|
||||
case "maximized":
|
||||
attributes.maximized = __parseBool(argValue);
|
||||
case "minimized":
|
||||
attributes.minimized = __parseBool(argValue);
|
||||
case "render-type", "renderer":
|
||||
attributes.context.type = argValue;
|
||||
case "render-version", "renderer-version":
|
||||
attributes.context.version = argValue;
|
||||
case "resizable":
|
||||
attributes.resizable = __parseBool(argValue);
|
||||
case "stencil", "stencil-buffer":
|
||||
attributes.context.stencil = __parseBool(argValue);
|
||||
// case "title": windowConfig.title = argValue;
|
||||
case "vsync":
|
||||
attributes.context.vsync = __parseBool(argValue);
|
||||
case "width":
|
||||
attributes.width = Std.parseInt(argValue);
|
||||
case "x":
|
||||
attributes.x = Std.parseInt(argValue);
|
||||
case "y":
|
||||
attributes.y = Std.parseInt(argValue);
|
||||
default:
|
||||
}
|
||||
}
|
||||
else if (!Reflect.hasField(attributes.parameters, parameter))
|
||||
{
|
||||
Reflect.setField(attributes.parameters, parameter, argValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#end
|
||||
|
||||
@:noCompletion private static inline function __parseBool(value:String):Bool
|
||||
{
|
||||
return (value == "true");
|
||||
}
|
||||
|
||||
@:noCompletion private static function __registerEntryPoint(projectName:String, entryPoint:Function):Void
|
||||
{
|
||||
// executes first!!
|
||||
#if (sys && !macro)
|
||||
funkin.backend.system.Main.preInit();
|
||||
#end
|
||||
|
||||
if (__applicationEntryPoint == null)
|
||||
{
|
||||
__applicationEntryPoint = new Map();
|
||||
}
|
||||
|
||||
__applicationEntryPoint[projectName] = entryPoint;
|
||||
}
|
||||
|
||||
@:noCompletion private static function __runProcess(command:String, args:Array<String> = null):String
|
||||
{
|
||||
#if sys
|
||||
try
|
||||
{
|
||||
if (args == null) args = [];
|
||||
|
||||
var process = new Process(command, args);
|
||||
var value = StringTools.trim(process.stdout.readLine().toString());
|
||||
process.close();
|
||||
return value;
|
||||
}
|
||||
catch (e:Dynamic) {}
|
||||
#end
|
||||
return null;
|
||||
}
|
||||
|
||||
// Get & Set Methods
|
||||
private static function get_allowScreenTimeout():Bool
|
||||
{
|
||||
#if (lime_cffi && !macro)
|
||||
return NativeCFFI.lime_system_get_allow_screen_timeout();
|
||||
#else
|
||||
return true;
|
||||
#end
|
||||
}
|
||||
|
||||
private static function set_allowScreenTimeout(value:Bool):Bool
|
||||
{
|
||||
#if (lime_cffi && !macro)
|
||||
return NativeCFFI.lime_system_set_allow_screen_timeout(value);
|
||||
#else
|
||||
return true;
|
||||
#end
|
||||
}
|
||||
|
||||
private static function get_applicationDirectory():String
|
||||
{
|
||||
if (__applicationDirectory == null)
|
||||
{
|
||||
__applicationDirectory = __getDirectory(APPLICATION);
|
||||
}
|
||||
|
||||
return __applicationDirectory;
|
||||
}
|
||||
|
||||
private static function get_applicationStorageDirectory():String
|
||||
{
|
||||
if (__applicationStorageDirectory == null)
|
||||
{
|
||||
__applicationStorageDirectory = __getDirectory(APPLICATION_STORAGE);
|
||||
}
|
||||
|
||||
return __applicationStorageDirectory;
|
||||
}
|
||||
|
||||
private static function get_deviceModel():String
|
||||
{
|
||||
if (__deviceModel == null)
|
||||
{
|
||||
#if (lime_cffi && !macro && (windows || ios || tvos))
|
||||
#if hl
|
||||
__deviceModel = @:privateAccess String.fromUTF8(NativeCFFI.lime_system_get_device_model());
|
||||
#else
|
||||
__deviceModel = NativeCFFI.lime_system_get_device_model();
|
||||
#end
|
||||
#elseif android
|
||||
var manufacturer:String = JNI.createStaticField("android/os/Build", "MANUFACTURER", "Ljava/lang/String;").get();
|
||||
var model:String = JNI.createStaticField("android/os/Build", "MODEL", "Ljava/lang/String;").get();
|
||||
if (manufacturer != null && model != null)
|
||||
{
|
||||
if (StringTools.startsWith(model.toLowerCase(), manufacturer.toLowerCase()))
|
||||
{
|
||||
model = StringTools.trim(model.substr(manufacturer.length));
|
||||
while (StringTools.startsWith(model, "-"))
|
||||
{
|
||||
model = StringTools.trim(model.substr(1));
|
||||
}
|
||||
}
|
||||
__deviceModel = model;
|
||||
}
|
||||
#elseif mac
|
||||
__deviceModel = __runProcess("sysctl", ["-n", "hw.model"]);
|
||||
#elseif linux
|
||||
__deviceModel = __runProcess("cat", ["/sys/devices/virtual/dmi/id/sys_vendor"]);
|
||||
#end
|
||||
}
|
||||
|
||||
return __deviceModel;
|
||||
}
|
||||
|
||||
private static function get_deviceVendor():String
|
||||
{
|
||||
if (__deviceVendor == null)
|
||||
{
|
||||
#if (lime_cffi && !macro && windows && !html5)
|
||||
#if hl
|
||||
__deviceVendor = @:privateAccess String.fromUTF8(NativeCFFI.lime_system_get_device_vendor());
|
||||
#else
|
||||
__deviceVendor = NativeCFFI.lime_system_get_device_vendor();
|
||||
#end
|
||||
#elseif android
|
||||
var vendor:String = JNI.createStaticField("android/os/Build", "MANUFACTURER", "Ljava/lang/String;").get();
|
||||
if (vendor != null)
|
||||
{
|
||||
__deviceVendor = vendor.charAt(0).toUpperCase() + vendor.substr(1);
|
||||
}
|
||||
#elseif (ios || mac || tvos)
|
||||
__deviceVendor = "Apple";
|
||||
#elseif linux
|
||||
__deviceVendor = __runProcess("cat", ["/sys/devices/virtual/dmi/id/product_name"]);
|
||||
#end
|
||||
}
|
||||
|
||||
return __deviceVendor;
|
||||
}
|
||||
|
||||
private static function get_desktopDirectory():String
|
||||
{
|
||||
if (__desktopDirectory == null)
|
||||
{
|
||||
__desktopDirectory = __getDirectory(DESKTOP);
|
||||
}
|
||||
|
||||
return __desktopDirectory;
|
||||
}
|
||||
|
||||
private static function get_documentsDirectory():String
|
||||
{
|
||||
if (__documentsDirectory == null)
|
||||
{
|
||||
__documentsDirectory = __getDirectory(DOCUMENTS);
|
||||
}
|
||||
|
||||
return __documentsDirectory;
|
||||
}
|
||||
|
||||
private static function get_endianness():Endian
|
||||
{
|
||||
if (__endianness == null)
|
||||
{
|
||||
#if (ps3 || wiiu || flash)
|
||||
__endianness = BIG_ENDIAN;
|
||||
#else
|
||||
var arrayBuffer = new ArrayBuffer(2);
|
||||
var uint8Array = new UInt8Array(arrayBuffer);
|
||||
var uint16array = new UInt16Array(arrayBuffer);
|
||||
uint8Array[0] = 0xAA;
|
||||
uint8Array[1] = 0xBB;
|
||||
if (uint16array[0] == 0xAABB) __endianness = BIG_ENDIAN;
|
||||
else
|
||||
__endianness = LITTLE_ENDIAN;
|
||||
#end
|
||||
}
|
||||
|
||||
return __endianness;
|
||||
}
|
||||
|
||||
private static function get_fontsDirectory():String
|
||||
{
|
||||
if (__fontsDirectory == null)
|
||||
{
|
||||
__fontsDirectory = __getDirectory(FONTS);
|
||||
}
|
||||
|
||||
return __fontsDirectory;
|
||||
}
|
||||
|
||||
private static function get_numDisplays():Int
|
||||
{
|
||||
#if (lime_cffi && !macro)
|
||||
return NativeCFFI.lime_system_get_num_displays();
|
||||
#else
|
||||
return 1;
|
||||
#end
|
||||
}
|
||||
|
||||
private static function get_platformLabel():String
|
||||
{
|
||||
if (__platformLabel == null)
|
||||
{
|
||||
#if (lime_cffi && !macro && windows && !html5)
|
||||
#if hl
|
||||
var label:String = @:privateAccess String.fromUTF8(NativeCFFI.lime_system_get_platform_label());
|
||||
#else
|
||||
var label:String = NativeCFFI.lime_system_get_platform_label();
|
||||
#end
|
||||
if (label != null) __platformLabel = StringTools.trim(label);
|
||||
#elseif linux
|
||||
__platformLabel = __runProcess("lsb_release", ["-ds"]);
|
||||
#else
|
||||
var name = System.platformName;
|
||||
var version = System.platformVersion;
|
||||
if (name != null && version != null) __platformLabel = name + " " + version;
|
||||
else if (name != null) __platformLabel = name;
|
||||
#end
|
||||
}
|
||||
|
||||
return __platformLabel;
|
||||
}
|
||||
|
||||
private static function get_platformName():String
|
||||
{
|
||||
if (__platformName == null)
|
||||
{
|
||||
#if windows
|
||||
__platformName = "Windows";
|
||||
#elseif mac
|
||||
__platformName = "macOS";
|
||||
#elseif linux
|
||||
__platformName = __runProcess("lsb_release", ["-is"]);
|
||||
#elseif ios
|
||||
__platformName = "iOS";
|
||||
#elseif android
|
||||
__platformName = "Android";
|
||||
#elseif air
|
||||
__platformName = "AIR";
|
||||
#elseif flash
|
||||
__platformName = "Flash Player";
|
||||
#elseif tvos
|
||||
__platformName = "tvOS";
|
||||
#elseif tizen
|
||||
__platformName = "Tizen";
|
||||
#elseif blackberry
|
||||
__platformName = "BlackBerry";
|
||||
#elseif firefox
|
||||
__platformName = "Firefox";
|
||||
#elseif webos
|
||||
__platformName = "webOS";
|
||||
#elseif nodejs
|
||||
__platformName = "Node.js";
|
||||
#elseif js
|
||||
__platformName = "HTML5";
|
||||
#end
|
||||
}
|
||||
|
||||
return __platformName;
|
||||
}
|
||||
|
||||
private static function get_platformVersion():String
|
||||
{
|
||||
if (__platformVersion == null)
|
||||
{
|
||||
#if (lime_cffi && !macro && windows && !html5)
|
||||
#if hl
|
||||
__platformVersion = @:privateAccess String.fromUTF8(NativeCFFI.lime_system_get_platform_version());
|
||||
#else
|
||||
__platformVersion = NativeCFFI.lime_system_get_platform_version();
|
||||
#end
|
||||
#elseif android
|
||||
var release = JNI.createStaticField("android/os/Build$VERSION", "RELEASE", "Ljava/lang/String;").get();
|
||||
var api = JNI.createStaticField("android/os/Build$VERSION", "SDK_INT", "I").get();
|
||||
if (release != null && api != null) __platformVersion = release + " (API " + api + ")";
|
||||
#elseif (lime_cffi && !macro && (ios || tvos))
|
||||
__platformVersion = NativeCFFI.lime_system_get_platform_version();
|
||||
#elseif mac
|
||||
__platformVersion = __runProcess("sw_vers", ["-productVersion"]);
|
||||
#elseif linux
|
||||
__platformVersion = __runProcess("lsb_release", ["-rs"]);
|
||||
#elseif flash
|
||||
__platformVersion = Capabilities.version;
|
||||
#end
|
||||
}
|
||||
|
||||
return __platformVersion;
|
||||
}
|
||||
|
||||
private static function get_userDirectory():String
|
||||
{
|
||||
if (__userDirectory == null)
|
||||
{
|
||||
__userDirectory = __getDirectory(USER);
|
||||
}
|
||||
|
||||
return __userDirectory;
|
||||
}
|
||||
}
|
||||
|
||||
#if (haxe_ver >= 4.0) enum #else @:enum #end abstract SystemDirectory(Int) from Int to Int from UInt to UInt
|
||||
{
|
||||
var APPLICATION = 0;
|
||||
var APPLICATION_STORAGE = 1;
|
||||
var DESKTOP = 2;
|
||||
var DOCUMENTS = 3;
|
||||
var FONTS = 4;
|
||||
var USER = 5;
|
||||
}
|
||||
@@ -1,411 +0,0 @@
|
||||
package lime.ui;
|
||||
|
||||
import haxe.io.Bytes;
|
||||
import haxe.io.Path;
|
||||
import lime._internal.backend.native.NativeCFFI;
|
||||
import lime.app.Event;
|
||||
import lime.graphics.Image;
|
||||
import lime.system.BackgroundWorker;
|
||||
import lime.utils.ArrayBuffer;
|
||||
import lime.utils.Resource;
|
||||
#if hl
|
||||
import hl.Bytes as HLBytes;
|
||||
import hl.NativeArray;
|
||||
#end
|
||||
#if sys
|
||||
import sys.io.File;
|
||||
#end
|
||||
#if (js && html5)
|
||||
import js.html.Blob;
|
||||
#end
|
||||
|
||||
/**
|
||||
Simple file dialog used for asking user where to save a file, or select files to open.
|
||||
|
||||
Example usage:
|
||||
```haxe
|
||||
var fileDialog = new FileDialog();
|
||||
|
||||
fileDialog.onCancel.add( () -> trace("Canceled.") );
|
||||
|
||||
fileDialog.onSave.add( path -> trace("File saved in " + path) );
|
||||
|
||||
fileDialog.onOpen.add( res -> trace("Size of the file = " + (res:haxe.io.Bytes).length) );
|
||||
|
||||
if ( fileDialog.open("jpg", null, "Load file") )
|
||||
trace("File dialog opened, waiting for selection...");
|
||||
else
|
||||
trace("This dialog is unsupported.");
|
||||
```
|
||||
|
||||
Availability note: most file dialog operations are only available on desktop targets, though
|
||||
`save()` is also available in HTML5.
|
||||
**/
|
||||
#if !lime_debug
|
||||
@:fileXml('tags="haxe,release"')
|
||||
@:noDebug
|
||||
#end
|
||||
@:access(lime._internal.backend.native.NativeCFFI)
|
||||
@:access(lime.graphics.Image)
|
||||
class FileDialog
|
||||
{
|
||||
/**
|
||||
Triggers when the user clicks "Cancel" during any operation, or when a function is unsupported
|
||||
(such as `open()` on HTML5).
|
||||
**/
|
||||
public var onCancel = new Event<Void->Void>();
|
||||
|
||||
/**
|
||||
Triggers when `open()` is successful. The `lime.utils.Resource` contains the file's data, and can
|
||||
be implicitly cast to `haxe.io.Bytes`.
|
||||
**/
|
||||
public var onOpen = new Event<Resource->Void>();
|
||||
|
||||
/**
|
||||
Triggers when `open()` is successful. The `lime.utils.Resource` contains the file's data, and can
|
||||
be implicitly cast to `haxe.io.Bytes`, the String is the path to the file.
|
||||
**/
|
||||
public var onOpenFile = new Event<(Resource, String)->Void>(); // Added by @NeeEoo
|
||||
|
||||
/**
|
||||
Triggers when `save()` is successful. The `String` is the path to the saved file.
|
||||
**/
|
||||
public var onSave = new Event<String->Void>();
|
||||
|
||||
/**
|
||||
Triggers when `browse()` is successful and `type` is anything other than
|
||||
`FileDialogType.OPEN_MULTIPLE`. The `String` is the path to the selected file.
|
||||
**/
|
||||
public var onSelect = new Event<String->Void>();
|
||||
|
||||
/**
|
||||
Triggers when `browse()` is successful and `type` is `FileDialogType.OPEN_MULTIPLE`. The
|
||||
`Array<String>` contains all selected file paths.
|
||||
**/
|
||||
public var onSelectMultiple = new Event<Array<String>->Void>();
|
||||
|
||||
public function new() {}
|
||||
|
||||
/**
|
||||
Opens a file selection dialog. If successful, either `onSelect` or `onSelectMultiple` will trigger
|
||||
with the result(s).
|
||||
|
||||
This function only works on desktop targets, and will return `false` otherwise.
|
||||
@param type Type of the file dialog: `OPEN`, `SAVE`, `OPEN_DIRECTORY` or `OPEN_MULTIPLE`.
|
||||
@param filter A filter to use when browsing. Asterisks are treated as wildcards. For example,
|
||||
`"*.jpg"` will match any file ending in `.jpg`.
|
||||
@param defaultPath The directory in which to start browsing and/or the default filename to
|
||||
suggest. Defaults to `Sys.getCwd()`, with no default filename.
|
||||
@param title The title to give the dialog window.
|
||||
@return Whether `browse()` is supported on this target.
|
||||
**/
|
||||
public function browse(type:FileDialogType = null, filter:String = null, defaultPath:String = null, title:String = null):Bool
|
||||
{
|
||||
if (type == null) type = FileDialogType.OPEN;
|
||||
|
||||
#if desktop
|
||||
var worker = new BackgroundWorker();
|
||||
|
||||
worker.doWork.add(function(_)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case OPEN:
|
||||
#if linux
|
||||
if (title == null) title = "Open File";
|
||||
#end
|
||||
|
||||
var path = null;
|
||||
#if (!macro && lime_cffi)
|
||||
#if hl
|
||||
var bytes = NativeCFFI.lime_file_dialog_open_file(title, filter, defaultPath);
|
||||
if (bytes != null)
|
||||
{
|
||||
path = @:privateAccess String.fromUTF8(cast bytes);
|
||||
}
|
||||
#else
|
||||
path = NativeCFFI.lime_file_dialog_open_file(title, filter, defaultPath);
|
||||
#end
|
||||
#end
|
||||
|
||||
worker.sendComplete(path);
|
||||
|
||||
case OPEN_MULTIPLE:
|
||||
#if linux
|
||||
if (title == null) title = "Open Files";
|
||||
#end
|
||||
|
||||
var paths = null;
|
||||
#if (!macro && lime_cffi)
|
||||
#if hl
|
||||
var bytes:NativeArray<HLBytes> = cast NativeCFFI.lime_file_dialog_open_files(title, filter, defaultPath);
|
||||
if (bytes != null)
|
||||
{
|
||||
paths = [];
|
||||
for (i in 0...bytes.length)
|
||||
{
|
||||
paths[i] = @:privateAccess String.fromUTF8(bytes[i]);
|
||||
}
|
||||
}
|
||||
#else
|
||||
paths = NativeCFFI.lime_file_dialog_open_files(title, filter, defaultPath);
|
||||
#end
|
||||
#end
|
||||
|
||||
worker.sendComplete(paths);
|
||||
|
||||
case OPEN_DIRECTORY:
|
||||
#if linux
|
||||
if (title == null) title = "Open Directory";
|
||||
#end
|
||||
|
||||
var path = null;
|
||||
#if (!macro && lime_cffi)
|
||||
#if hl
|
||||
var bytes = NativeCFFI.lime_file_dialog_open_directory(title, filter, defaultPath);
|
||||
if (bytes != null)
|
||||
{
|
||||
path = @:privateAccess String.fromUTF8(cast bytes);
|
||||
}
|
||||
#else
|
||||
path = NativeCFFI.lime_file_dialog_open_directory(title, filter, defaultPath);
|
||||
#end
|
||||
#end
|
||||
|
||||
worker.sendComplete(path);
|
||||
|
||||
case SAVE:
|
||||
#if linux
|
||||
if (title == null) title = "Save File";
|
||||
#end
|
||||
|
||||
var path = null;
|
||||
#if (!macro && lime_cffi)
|
||||
#if hl
|
||||
var bytes = NativeCFFI.lime_file_dialog_save_file(title, filter, defaultPath);
|
||||
if (bytes != null)
|
||||
{
|
||||
path = @:privateAccess String.fromUTF8(cast bytes);
|
||||
}
|
||||
#else
|
||||
path = NativeCFFI.lime_file_dialog_save_file(title, filter, defaultPath);
|
||||
#end
|
||||
#end
|
||||
|
||||
worker.sendComplete(path);
|
||||
}
|
||||
});
|
||||
|
||||
worker.onComplete.add(function(result)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case OPEN, OPEN_DIRECTORY, SAVE:
|
||||
var path:String = cast result;
|
||||
|
||||
if (path != null)
|
||||
{
|
||||
// Makes sure the filename ends with extension
|
||||
if (type == SAVE && filter != null && path.indexOf(".") == -1)
|
||||
{
|
||||
path += "." + filter;
|
||||
}
|
||||
|
||||
onSelect.dispatch(path);
|
||||
}
|
||||
else
|
||||
{
|
||||
onCancel.dispatch();
|
||||
}
|
||||
|
||||
case OPEN_MULTIPLE:
|
||||
var paths:Array<String> = cast result;
|
||||
|
||||
if (paths != null && paths.length > 0)
|
||||
{
|
||||
onSelectMultiple.dispatch(paths);
|
||||
}
|
||||
else
|
||||
{
|
||||
onCancel.dispatch();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
worker.run();
|
||||
|
||||
return true;
|
||||
#else
|
||||
onCancel.dispatch();
|
||||
return false;
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
Shows an open file dialog. If successful, `onOpen` will trigger with the file contents.
|
||||
|
||||
This function only works on desktop targets, and will return `false` otherwise.
|
||||
@param filter A filter to use when browsing. Asterisks are treated as wildcards. For example,
|
||||
`"*.jpg"` will match any file ending in `.jpg`.
|
||||
@param defaultPath The directory in which to start browsing and/or the default filename to
|
||||
suggest. Defaults to `Sys.getCwd()`, with no default filename.
|
||||
@param title The title to give the dialog window.
|
||||
@return Whether `open()` is supported on this target.
|
||||
**/
|
||||
public function open(filter:String = null, defaultPath:String = null, title:String = null):Bool
|
||||
{
|
||||
#if (desktop && sys)
|
||||
var worker = new BackgroundWorker();
|
||||
|
||||
worker.doWork.add(function(_)
|
||||
{
|
||||
#if linux
|
||||
if (title == null) title = "Open File";
|
||||
#end
|
||||
|
||||
var path = null;
|
||||
#if (!macro && lime_cffi)
|
||||
#if hl
|
||||
var bytes = NativeCFFI.lime_file_dialog_open_file(title, filter, defaultPath);
|
||||
if (bytes != null) path = @:privateAccess String.fromUTF8(cast bytes);
|
||||
#else
|
||||
path = NativeCFFI.lime_file_dialog_open_file(title, filter, defaultPath);
|
||||
#end
|
||||
#end
|
||||
|
||||
worker.sendComplete(path);
|
||||
});
|
||||
|
||||
worker.onComplete.add(function(path:String)
|
||||
{
|
||||
if (path != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var data = File.getBytes(path);
|
||||
onOpen.dispatch(data);
|
||||
onOpenFile.dispatch(data, path); // Added by @NeeEoo
|
||||
return;
|
||||
}
|
||||
catch (e:Dynamic) {}
|
||||
}
|
||||
|
||||
onCancel.dispatch();
|
||||
});
|
||||
|
||||
worker.run();
|
||||
|
||||
return true;
|
||||
#else
|
||||
onCancel.dispatch();
|
||||
return false;
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
Shows an open file dialog. If successful, `onSave` will trigger with the selected path.
|
||||
|
||||
This function only works on desktop and HMTL5 targets, and will return `false` otherwise.
|
||||
@param data The file contents, in `haxe.io.Bytes` format. (Implicit casting possible.)
|
||||
@param filter A filter to use when browsing. Asterisks are treated as wildcards. For example,
|
||||
`"*.jpg"` will match any file ending in `.jpg`. Used only if targeting deskop.
|
||||
@param defaultPath The directory in which to start browsing and/or the default filename to
|
||||
suggest. When targeting destkop, this defaults to `Sys.getCwd()` with no default filename. When targeting
|
||||
HTML5, this defaults to the browser's download directory, with a default filename based on the MIME type.
|
||||
@param title The title to give the dialog window.
|
||||
@param type The default MIME type of the file, in case the type can't be determined from the
|
||||
file data. Used only if targeting HTML5.
|
||||
@return Whether `save()` is supported on this target.
|
||||
**/
|
||||
public function save(data:Resource, filter:String = null, defaultPath:String = null, title:String = null, type:String = "application/octet-stream"):Bool
|
||||
{
|
||||
if (data == null)
|
||||
{
|
||||
onCancel.dispatch();
|
||||
return false;
|
||||
}
|
||||
|
||||
#if (desktop && sys)
|
||||
var worker = new BackgroundWorker();
|
||||
|
||||
worker.doWork.add(function(_)
|
||||
{
|
||||
#if linux
|
||||
if (title == null) title = "Save File";
|
||||
#end
|
||||
|
||||
var path = null;
|
||||
#if (!macro && lime_cffi)
|
||||
#if hl
|
||||
var bytes = NativeCFFI.lime_file_dialog_save_file(title, filter, defaultPath);
|
||||
path = @:privateAccess String.fromUTF8(cast bytes);
|
||||
#else
|
||||
path = NativeCFFI.lime_file_dialog_save_file(title, filter, defaultPath);
|
||||
#end
|
||||
#end
|
||||
|
||||
worker.sendComplete(path);
|
||||
});
|
||||
|
||||
worker.onComplete.add(function(path:String)
|
||||
{
|
||||
if (path != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
File.saveBytes(path, data);
|
||||
onSave.dispatch(path);
|
||||
return;
|
||||
}
|
||||
catch (e:Dynamic) {}
|
||||
}
|
||||
|
||||
onCancel.dispatch();
|
||||
});
|
||||
|
||||
worker.run();
|
||||
|
||||
return true;
|
||||
#elseif (js && html5)
|
||||
// TODO: Cleaner API for mimeType detection
|
||||
|
||||
var defaultExtension = "";
|
||||
|
||||
if (Image.__isPNG(data))
|
||||
{
|
||||
type = "image/png";
|
||||
defaultExtension = ".png";
|
||||
}
|
||||
else if (Image.__isJPG(data))
|
||||
{
|
||||
type = "image/jpeg";
|
||||
defaultExtension = ".jpg";
|
||||
}
|
||||
else if (Image.__isGIF(data))
|
||||
{
|
||||
type = "image/gif";
|
||||
defaultExtension = ".gif";
|
||||
}
|
||||
else if (Image.__isWebP(data))
|
||||
{
|
||||
type = "image/webp";
|
||||
defaultExtension = ".webp";
|
||||
}
|
||||
|
||||
var path = defaultPath != null ? Path.withoutDirectory(defaultPath) : "download" + defaultExtension;
|
||||
var buffer = (data : Bytes).getData();
|
||||
buffer = buffer.slice(0, (data : Bytes).length);
|
||||
|
||||
#if commonjs
|
||||
untyped #if haxe4 js.Syntax.code #else __js__ #end ("require ('file-saver')")(new Blob([buffer], {type: type}), path, true);
|
||||
#else
|
||||
untyped window.saveAs(new Blob([buffer], {type: type}), path, true);
|
||||
#end
|
||||
onSave.dispatch(path);
|
||||
return true;
|
||||
#else
|
||||
onCancel.dispatch();
|
||||
return false;
|
||||
#end
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,8 @@ class Log
|
||||
{
|
||||
#if js
|
||||
untyped #if haxe4 js.Syntax.code #else __js__ #end ("console").debug("[" + info.className + "] " + message);
|
||||
#elseif !macro
|
||||
FunkinLogs.trace('[${info.className}] $message', INFO, LIGHTGRAY);
|
||||
#else
|
||||
println("[" + info.className + "] " + Std.string(message));
|
||||
#end
|
||||
@@ -53,7 +55,7 @@ class Log
|
||||
if (level >= LogLevel.INFO)
|
||||
{
|
||||
#if !macro
|
||||
FunkinLogs.trace('[${info.className}] $message', INFO, RED);
|
||||
FunkinLogs.trace('[${info.className}] $message', INFO, CYAN);
|
||||
#else
|
||||
println("[" + info.className + "] " + Std.string(message));
|
||||
#end
|
||||
|
||||
@@ -1,347 +0,0 @@
|
||||
package lime.utils;
|
||||
|
||||
import haxe.ds.ObjectMap;
|
||||
|
||||
|
||||
/**
|
||||
A generic object pool for reusing objects.
|
||||
**/
|
||||
#if !lime_debug
|
||||
@:fileXml('tags="haxe,release"')
|
||||
@:noDebug
|
||||
#end
|
||||
#if !js @:generic #end class ObjectPool<T>
|
||||
{
|
||||
/**
|
||||
The number of active objects in the pool.
|
||||
**/
|
||||
public var activeObjects(default, null):Int;
|
||||
|
||||
/**
|
||||
The number of inactive objects in the pool.
|
||||
**/
|
||||
public var inactiveObjects(default, null):Int;
|
||||
|
||||
/**
|
||||
The total size of the object pool (both active and inactive objects).
|
||||
**/
|
||||
public var size(get, set):Null<Int>;
|
||||
|
||||
@:noCompletion private var __inactiveObject0:T;
|
||||
@:noCompletion private var __inactiveObject1:T;
|
||||
@:noCompletion private var __inactiveObjectList:List<T>;
|
||||
@:noCompletion private var __pool:Map<T, Bool>;
|
||||
@:noCompletion private var __size:Null<Int>;
|
||||
|
||||
/**
|
||||
Creates a new ObjectPool instance.
|
||||
|
||||
@param create A function that creates a new instance of type T.
|
||||
@param clean A function that cleans up an instance of type T before it is reused.
|
||||
@param size The maximum size of the object pool.
|
||||
**/
|
||||
public function new(create:Void->T = null, clean:T->Void = null, size:Null<Int> = null)
|
||||
{
|
||||
__pool = cast new ObjectMap();
|
||||
|
||||
activeObjects = 0;
|
||||
inactiveObjects = 0;
|
||||
|
||||
__inactiveObject0 = null;
|
||||
__inactiveObject1 = null;
|
||||
__inactiveObjectList = new List<T>();
|
||||
|
||||
if (create != null)
|
||||
{
|
||||
this.create = create;
|
||||
}
|
||||
if (clean != null)
|
||||
{
|
||||
this.clean = clean;
|
||||
}
|
||||
if (size != null)
|
||||
{
|
||||
this.size = size;
|
||||
}
|
||||
}
|
||||
/**
|
||||
Adds an object to the object pool.
|
||||
|
||||
@param object The object to add to the pool.
|
||||
**/
|
||||
public function add(object:T):Void
|
||||
{
|
||||
if (object != null && !__pool.exists(object))
|
||||
{
|
||||
__pool.set(object, false);
|
||||
clean(object);
|
||||
__addInactive(object);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Dynamic function.
|
||||
|
||||
Cleans up an object before returning it to the pool.
|
||||
|
||||
@param object The object to clean up.
|
||||
**/
|
||||
public dynamic function clean(object:T):Void {}
|
||||
|
||||
/**
|
||||
Clears the object pool, removing all objects.
|
||||
**/
|
||||
public function clear():Void
|
||||
{
|
||||
__pool = cast new ObjectMap();
|
||||
|
||||
activeObjects = 0;
|
||||
inactiveObjects = 0;
|
||||
|
||||
__inactiveObject0 = null;
|
||||
__inactiveObject1 = null;
|
||||
__inactiveObjectList.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
Dynamic function.
|
||||
|
||||
Creates a new Object.
|
||||
**/
|
||||
public dynamic function create():T
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
Creates a new object and adds it to the pool, or returns an existing inactive object from the pool.
|
||||
|
||||
@return The object retrieved from the pool, or null if the pool is full and no new objects can be created.
|
||||
**/
|
||||
public function get():T
|
||||
{
|
||||
var object = null;
|
||||
|
||||
if (inactiveObjects > 0)
|
||||
{
|
||||
object = __getInactive();
|
||||
}
|
||||
else if (__size == null || activeObjects < __size)
|
||||
{
|
||||
object = create();
|
||||
|
||||
if (object != null)
|
||||
{
|
||||
__pool.set(object, true);
|
||||
activeObjects++;
|
||||
}
|
||||
}
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
/**
|
||||
Releases an active object back into the pool.
|
||||
|
||||
@param object The object to release.
|
||||
**/
|
||||
public function release(object:T):Void
|
||||
{
|
||||
#if lime_pool_debug
|
||||
if (object == null || !__pool.exists(object))
|
||||
{
|
||||
Log.error("Object is not a member of the pool");
|
||||
}
|
||||
else if (!__pool.get(object))
|
||||
{
|
||||
Log.error("Object has already been released");
|
||||
}
|
||||
#end
|
||||
|
||||
activeObjects--;
|
||||
|
||||
if (__size == null || activeObjects + inactiveObjects < __size)
|
||||
{
|
||||
clean(object);
|
||||
__addInactive(object);
|
||||
}
|
||||
else
|
||||
{
|
||||
__pool.remove(object);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Removes an object from the pool.
|
||||
|
||||
@param object The object to remove from the pool.
|
||||
**/
|
||||
public function remove(object:T):Void
|
||||
{
|
||||
if (object != null && __pool.exists(object))
|
||||
{
|
||||
__pool.remove(object);
|
||||
|
||||
if (__inactiveObject0 == object)
|
||||
{
|
||||
__inactiveObject0 = null;
|
||||
inactiveObjects--;
|
||||
}
|
||||
else if (__inactiveObject1 == object)
|
||||
{
|
||||
__inactiveObject1 = null;
|
||||
inactiveObjects--;
|
||||
}
|
||||
else if (__inactiveObjectList.remove(object))
|
||||
{
|
||||
inactiveObjects--;
|
||||
}
|
||||
else
|
||||
{
|
||||
activeObjects--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@:noCompletion private inline function __addInactive(object:T):Void
|
||||
{
|
||||
#if lime_pool_debug
|
||||
__pool.set(object, false);
|
||||
#end
|
||||
|
||||
if (__inactiveObject0 == null)
|
||||
{
|
||||
__inactiveObject0 = object;
|
||||
}
|
||||
else if (__inactiveObject1 == null)
|
||||
{
|
||||
__inactiveObject1 = object;
|
||||
}
|
||||
else
|
||||
{
|
||||
__inactiveObjectList.add(object);
|
||||
}
|
||||
|
||||
inactiveObjects++;
|
||||
}
|
||||
|
||||
@:noCompletion private inline function __getInactive():T
|
||||
{
|
||||
var object = null;
|
||||
|
||||
if (__inactiveObject0 != null)
|
||||
{
|
||||
object = __inactiveObject0;
|
||||
__inactiveObject0 = null;
|
||||
}
|
||||
else if (__inactiveObject1 != null)
|
||||
{
|
||||
object = __inactiveObject1;
|
||||
__inactiveObject1 = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
object = __inactiveObjectList.pop();
|
||||
|
||||
if (__inactiveObjectList.length > 0)
|
||||
{
|
||||
__inactiveObject0 = __inactiveObjectList.pop();
|
||||
}
|
||||
|
||||
if (__inactiveObjectList.length > 0)
|
||||
{
|
||||
__inactiveObject1 = __inactiveObjectList.pop();
|
||||
}
|
||||
}
|
||||
|
||||
#if lime_pool_debug
|
||||
__pool.set(object, true);
|
||||
#end
|
||||
|
||||
inactiveObjects--;
|
||||
activeObjects++;
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
@:noCompletion private function __removeInactive(count:Int):Void
|
||||
{
|
||||
if (count <= 0 || inactiveObjects == 0) return;
|
||||
|
||||
if (__inactiveObject0 != null)
|
||||
{
|
||||
__pool.remove(__inactiveObject0);
|
||||
__inactiveObject0 = null;
|
||||
inactiveObjects--;
|
||||
count--;
|
||||
}
|
||||
|
||||
if (count == 0 || inactiveObjects == 0) return;
|
||||
|
||||
if (__inactiveObject1 != null)
|
||||
{
|
||||
__pool.remove(__inactiveObject1);
|
||||
__inactiveObject1 = null;
|
||||
inactiveObjects--;
|
||||
count--;
|
||||
}
|
||||
|
||||
if (count == 0 || inactiveObjects == 0) return;
|
||||
|
||||
for (object in __inactiveObjectList)
|
||||
{
|
||||
__pool.remove(object);
|
||||
__inactiveObjectList.remove(object);
|
||||
inactiveObjects--;
|
||||
count--;
|
||||
|
||||
if (count == 0 || inactiveObjects == 0) return;
|
||||
}
|
||||
}
|
||||
|
||||
// Get & Set Methods
|
||||
@:noCompletion private function get_size():Null<Int>
|
||||
{
|
||||
return __size;
|
||||
}
|
||||
|
||||
@:noCompletion private function set_size(value:Null<Int>):Null<Int>
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
__size = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
var current = inactiveObjects + activeObjects;
|
||||
__size = value;
|
||||
|
||||
if (current > value)
|
||||
{
|
||||
__removeInactive(current - value);
|
||||
}
|
||||
else if (value > current)
|
||||
{
|
||||
var object;
|
||||
|
||||
for (i in 0...(value - current))
|
||||
{
|
||||
object = create();
|
||||
|
||||
if (object != null)
|
||||
{
|
||||
__pool.set(object, false);
|
||||
__inactiveObjectList.add(object);
|
||||
inactiveObjects++;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@@ -1,864 +0,0 @@
|
||||
package openfl.display;
|
||||
|
||||
#if !flash
|
||||
import openfl.display._internal.Context3DGraphics;
|
||||
import openfl.display.Bitmap;
|
||||
import openfl.display.DisplayObject;
|
||||
import openfl.display.Tilemap;
|
||||
import openfl.events.EventDispatcher;
|
||||
import openfl.events.RenderEvent;
|
||||
import openfl.geom.ColorTransform;
|
||||
import openfl.geom.Matrix;
|
||||
import openfl.geom.Point;
|
||||
import openfl.geom.Rectangle;
|
||||
import openfl.text.TextField;
|
||||
#if lime
|
||||
import lime._internal.graphics.ImageCanvasUtil; // TODO
|
||||
import lime.graphics.cairo.Cairo;
|
||||
import lime.graphics.RenderContext;
|
||||
import lime.graphics.RenderContextType;
|
||||
#end
|
||||
|
||||
#if !openfl_debug
|
||||
@:fileXml('tags="haxe,release"')
|
||||
@:noDebug
|
||||
#end
|
||||
@:access(openfl.display._internal.Context3DGraphics)
|
||||
@:access(lime.graphics.ImageBuffer)
|
||||
@:access(openfl.display.Bitmap)
|
||||
@:access(openfl.display.BitmapData)
|
||||
@:access(openfl.display.DisplayObject)
|
||||
@:access(openfl.display.Graphics)
|
||||
@:access(openfl.display.Tilemap)
|
||||
@:access(openfl.display3D.Context3D)
|
||||
@:access(openfl.events.RenderEvent)
|
||||
@:access(openfl.filters.BitmapFilter)
|
||||
@:access(openfl.geom.ColorTransform)
|
||||
@:access(openfl.geom.Rectangle)
|
||||
@:access(openfl.geom.Transform)
|
||||
@:access(openfl.text.TextField)
|
||||
@:allow(openfl.display._internal)
|
||||
@:allow(openfl.display)
|
||||
@:allow(openfl.text)
|
||||
class DisplayObjectRenderer extends EventDispatcher
|
||||
{
|
||||
@:noCompletion private var __allowSmoothing:Bool;
|
||||
@:noCompletion private var __blendMode:BlendMode;
|
||||
@:noCompletion private var __cleared:Bool;
|
||||
@SuppressWarnings("checkstyle:Dynamic") @:noCompletion private var __context:#if lime RenderContext #else Dynamic #end;
|
||||
@:noCompletion private var __overrideBlendMode:BlendMode;
|
||||
@:noCompletion private var __pixelRatio:Float;
|
||||
@:noCompletion private var __roundPixels:Bool;
|
||||
@:noCompletion private var __stage:Stage;
|
||||
@:noCompletion private var __tempColorTransform:ColorTransform;
|
||||
@:noCompletion private var __transparent:Bool;
|
||||
@SuppressWarnings("checkstyle:Dynamic") @:noCompletion private var __type:#if lime RenderContextType #else Dynamic #end;
|
||||
@:noCompletion private var __worldAlpha:Float;
|
||||
@:noCompletion private var __worldColorTransform:ColorTransform;
|
||||
@:noCompletion private var __worldTransform:Matrix;
|
||||
|
||||
@:noCompletion private function new()
|
||||
{
|
||||
super();
|
||||
|
||||
__allowSmoothing = true;
|
||||
__pixelRatio = 1;
|
||||
__tempColorTransform = new ColorTransform();
|
||||
__worldAlpha = 1;
|
||||
}
|
||||
|
||||
@:noCompletion private function __clear():Void {}
|
||||
|
||||
@:noCompletion private function __getAlpha(value:Float):Float
|
||||
{
|
||||
return value * __worldAlpha;
|
||||
}
|
||||
|
||||
@:noCompletion private function __getColorTransform(value:ColorTransform):ColorTransform
|
||||
{
|
||||
if (__worldColorTransform != null)
|
||||
{
|
||||
__tempColorTransform.__copyFrom(__worldColorTransform);
|
||||
__tempColorTransform.__combine(value);
|
||||
return __tempColorTransform;
|
||||
}
|
||||
else
|
||||
{
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
@:noCompletion private function __popMask():Void {}
|
||||
|
||||
@:noCompletion private function __popMaskObject(object:DisplayObject, handleScrollRect:Bool = true):Void {}
|
||||
|
||||
@:noCompletion private function __popMaskRect():Void {}
|
||||
|
||||
@:noCompletion private function __pushMask(mask:DisplayObject):Void {}
|
||||
|
||||
@:noCompletion private function __pushMaskObject(object:DisplayObject, handleScrollRect:Bool = true):Void {}
|
||||
|
||||
@:noCompletion private function __pushMaskRect(rect:Rectangle, transform:Matrix):Void {}
|
||||
|
||||
@:noCompletion private function __render(object:IBitmapDrawable):Void {}
|
||||
|
||||
@:noCompletion private function __renderEvent(displayObject:DisplayObject):Void
|
||||
{
|
||||
var renderer = this;
|
||||
#if lime
|
||||
if (displayObject.__customRenderEvent != null && displayObject.__renderable)
|
||||
{
|
||||
displayObject.__customRenderEvent.allowSmoothing = renderer.__allowSmoothing;
|
||||
displayObject.__customRenderEvent.objectMatrix.copyFrom(displayObject.__renderTransform);
|
||||
displayObject.__customRenderEvent.objectColorTransform.__copyFrom(displayObject.__worldColorTransform);
|
||||
displayObject.__customRenderEvent.renderer = renderer;
|
||||
|
||||
switch (renderer.__type)
|
||||
{
|
||||
case OPENGL:
|
||||
if (!renderer.__cleared) renderer.__clear();
|
||||
|
||||
var renderer:OpenGLRenderer = cast renderer;
|
||||
renderer.setShader(displayObject.__worldShader);
|
||||
renderer.__context3D.__flushGL();
|
||||
|
||||
displayObject.__customRenderEvent.type = RenderEvent.RENDER_OPENGL;
|
||||
|
||||
case CAIRO:
|
||||
displayObject.__customRenderEvent.type = RenderEvent.RENDER_CAIRO;
|
||||
|
||||
case DOM:
|
||||
if (displayObject.stage != null && displayObject.__worldVisible)
|
||||
{
|
||||
displayObject.__customRenderEvent.type = RenderEvent.RENDER_DOM;
|
||||
}
|
||||
else
|
||||
{
|
||||
displayObject.__customRenderEvent.type = RenderEvent.CLEAR_DOM;
|
||||
}
|
||||
|
||||
case CANVAS:
|
||||
displayObject.__customRenderEvent.type = RenderEvent.RENDER_CANVAS;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
renderer.__setBlendMode(displayObject.__worldBlendMode);
|
||||
renderer.__pushMaskObject(displayObject);
|
||||
|
||||
displayObject.dispatchEvent(displayObject.__customRenderEvent);
|
||||
|
||||
renderer.__popMaskObject(displayObject);
|
||||
|
||||
if (renderer.__type == OPENGL)
|
||||
{
|
||||
var renderer:OpenGLRenderer = cast renderer;
|
||||
renderer.setViewport();
|
||||
}
|
||||
}
|
||||
#end
|
||||
}
|
||||
|
||||
@:noCompletion private function __resize(width:Int, height:Int):Void {}
|
||||
|
||||
@:noCompletion private function __setBlendMode(value:BlendMode):Void {}
|
||||
|
||||
@:noCompletion private function __shouldCacheHardware(displayObject:DisplayObject, value:Null<Bool>):Null<Bool>
|
||||
{
|
||||
if (displayObject == null) return null;
|
||||
|
||||
switch (displayObject.__drawableType)
|
||||
{
|
||||
case SPRITE, STAGE:
|
||||
if (value == true) return true;
|
||||
value = __shouldCacheHardware_DisplayObject(displayObject, value);
|
||||
if (value == true) return true;
|
||||
|
||||
if (displayObject.__children != null)
|
||||
{
|
||||
for (child in displayObject.__children)
|
||||
{
|
||||
value = __shouldCacheHardware_DisplayObject(child, value);
|
||||
if (value == true) return true;
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
|
||||
case TEXT_FIELD:
|
||||
return value == true ? true : false;
|
||||
|
||||
case TILEMAP:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return __shouldCacheHardware_DisplayObject(displayObject, value);
|
||||
}
|
||||
}
|
||||
|
||||
@:noCompletion private function __shouldCacheHardware_DisplayObject(displayObject:DisplayObject, value:Null<Bool>):Null<Bool>
|
||||
{
|
||||
if (value == true || displayObject.__filters != null) return true;
|
||||
|
||||
if (value == false || (displayObject.__graphics != null && !Context3DGraphics.isCompatible(displayObject.__graphics)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@:noCompletion private function __updateCacheBitmap(displayObject:DisplayObject, force:Bool):Bool
|
||||
{
|
||||
if (displayObject == null) return false;
|
||||
var renderer = this;
|
||||
|
||||
switch (displayObject.__drawableType)
|
||||
{
|
||||
case BITMAP:
|
||||
var bitmap:Bitmap = cast displayObject;
|
||||
// TODO: Handle filters without an intermediate draw
|
||||
if (bitmap.__bitmapData == null
|
||||
|| (bitmap.__filters == null #if lime && renderer.__type == OPENGL #end && bitmap.__cacheBitmap == null)) return false;
|
||||
force = (bitmap.__bitmapData.image != null && bitmap.__bitmapData.image.version != bitmap.__imageVersion);
|
||||
|
||||
case TEXT_FIELD:
|
||||
var textField:TextField = cast displayObject;
|
||||
if (textField.__filters == null #if lime && renderer.__type == OPENGL #end && textField.__cacheBitmap == null
|
||||
&& !textField.__domRender) return false;
|
||||
if (force) textField.__renderDirty = true;
|
||||
force = force || textField.__dirty;
|
||||
|
||||
case TILEMAP:
|
||||
var tilemap:Tilemap = cast displayObject;
|
||||
if (tilemap.__filters == null #if lime && renderer.__type == OPENGL #end && tilemap.__cacheBitmap == null) return false;
|
||||
|
||||
default:
|
||||
}
|
||||
|
||||
#if lime
|
||||
if (displayObject.__isCacheBitmapRender) return false;
|
||||
#if openfl_disable_cacheasbitmap
|
||||
return false;
|
||||
#end
|
||||
|
||||
var colorTransform = ColorTransform.__pool.get();
|
||||
colorTransform.__copyFrom(displayObject.__worldColorTransform);
|
||||
if (renderer.__worldColorTransform != null) colorTransform.__combine(renderer.__worldColorTransform);
|
||||
var updated = false;
|
||||
|
||||
// TODO: Do not force cacheAsBitmap on OpenGL once Scale-9 is properly supported in Context3DShape
|
||||
if (displayObject.cacheAsBitmap
|
||||
|| (renderer.__type != OPENGL && !colorTransform.__isDefault(true))
|
||||
|| (renderer.__type == OPENGL && displayObject.scale9Grid != null))
|
||||
{
|
||||
var rect = null;
|
||||
|
||||
var needRender = (displayObject.__cacheBitmap == null
|
||||
|| (displayObject.__renderDirty && (force || (displayObject.__children != null && displayObject.__children.length > 0)))
|
||||
|| displayObject.opaqueBackground != displayObject.__cacheBitmapBackground);
|
||||
var softwareDirty = needRender
|
||||
|| (displayObject.__graphics != null && displayObject.__graphics.__softwareDirty)
|
||||
|| !displayObject.__cacheBitmapColorTransform.__equals(colorTransform, true);
|
||||
var hardwareDirty = needRender || (displayObject.__graphics != null && displayObject.__graphics.__hardwareDirty);
|
||||
|
||||
var renderType = renderer.__type;
|
||||
|
||||
if (softwareDirty || hardwareDirty)
|
||||
{
|
||||
#if !openfl_force_gl_cacheasbitmap
|
||||
if (renderType == OPENGL)
|
||||
{
|
||||
if (#if !openfl_disable_gl_cacheasbitmap __shouldCacheHardware(displayObject, null) == false #else true #end)
|
||||
{
|
||||
#if (js && html5)
|
||||
renderType = CANVAS;
|
||||
#else
|
||||
renderType = CAIRO;
|
||||
#end
|
||||
}
|
||||
}
|
||||
#end
|
||||
|
||||
if (softwareDirty && (renderType == CANVAS || renderType == CAIRO)) needRender = true;
|
||||
if (hardwareDirty && renderType == OPENGL) needRender = true;
|
||||
}
|
||||
|
||||
var updateTransform = (needRender || !displayObject.__cacheBitmap.__worldTransform.equals(displayObject.__worldTransform));
|
||||
var hasFilters = #if !openfl_disable_filters displayObject.__filters != null #else false #end;
|
||||
|
||||
#if !openfl_enable_cacheasbitmap
|
||||
if (renderer.__type == DOM && !hasFilters)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#end
|
||||
|
||||
if (hasFilters && !needRender)
|
||||
{
|
||||
for (filter in displayObject.__filters)
|
||||
{
|
||||
if (filter.__renderDirty)
|
||||
{
|
||||
needRender = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (displayObject.__cacheBitmapMatrix == null)
|
||||
{
|
||||
displayObject.__cacheBitmapMatrix = new Matrix();
|
||||
}
|
||||
|
||||
var bitmapMatrix = (displayObject.__cacheAsBitmapMatrix != null ? displayObject.__cacheAsBitmapMatrix : displayObject.__renderTransform);
|
||||
|
||||
if (!needRender
|
||||
&& (bitmapMatrix.a != displayObject.__cacheBitmapMatrix.a
|
||||
|| bitmapMatrix.b != displayObject.__cacheBitmapMatrix.b
|
||||
|| bitmapMatrix.c != displayObject.__cacheBitmapMatrix.c
|
||||
|| bitmapMatrix.d != displayObject.__cacheBitmapMatrix.d))
|
||||
{
|
||||
needRender = true;
|
||||
}
|
||||
|
||||
if (!needRender
|
||||
&& renderer.__type != OPENGL
|
||||
&& displayObject.__cacheBitmapData != null
|
||||
&& displayObject.__cacheBitmapData.image != null
|
||||
&& displayObject.__cacheBitmapData.image.version < displayObject.__cacheBitmapData.__textureVersion)
|
||||
{
|
||||
needRender = true;
|
||||
}
|
||||
|
||||
// Ensure that cached bitmap is updated after changes to scrollRect
|
||||
if (!needRender)
|
||||
{
|
||||
var current = displayObject;
|
||||
while (current != null)
|
||||
{
|
||||
if (current.scrollRect != null)
|
||||
{
|
||||
// TODO: do we need to update transform if scroll rects haven't changed?
|
||||
updateTransform = true;
|
||||
break;
|
||||
}
|
||||
current = current.parent;
|
||||
}
|
||||
}
|
||||
|
||||
displayObject.__cacheBitmapMatrix.copyFrom(bitmapMatrix);
|
||||
displayObject.__cacheBitmapMatrix.tx = 0;
|
||||
displayObject.__cacheBitmapMatrix.ty = 0;
|
||||
|
||||
// TODO: Handle dimensions better if object has a scrollRect?
|
||||
|
||||
var bitmapWidth = 0, bitmapHeight = 0;
|
||||
var filterWidth = 0, filterHeight = 0;
|
||||
var offsetX = 0., offsetY = 0.;
|
||||
|
||||
#if (openfl_disable_hdpi || openfl_disable_hdpi_cacheasbitmap)
|
||||
var pixelRatio = 1;
|
||||
#else
|
||||
var pixelRatio = __pixelRatio;
|
||||
#end
|
||||
|
||||
if (updateTransform || needRender)
|
||||
{
|
||||
rect = Rectangle.__pool.get();
|
||||
|
||||
displayObject.__getFilterBounds(rect, displayObject.__cacheBitmapMatrix);
|
||||
|
||||
filterWidth = rect.width > 0 ? Math.floor(rect.width * pixelRatio) : 0;
|
||||
filterHeight = rect.height > 0 ? Math.floor(rect.height * pixelRatio) : 0;
|
||||
|
||||
offsetX = rect.x > 0 ? Math.ceil(rect.x) : Math.floor(rect.x);
|
||||
offsetY = rect.y > 0 ? Math.ceil(rect.y) : Math.floor(rect.y);
|
||||
|
||||
if (displayObject.__cacheBitmapData != null)
|
||||
{
|
||||
if (filterWidth > displayObject.__cacheBitmapData.width || filterHeight > displayObject.__cacheBitmapData.height)
|
||||
{
|
||||
bitmapWidth = Math.ceil(Math.max(filterWidth * 1.25, displayObject.__cacheBitmapData.width));
|
||||
bitmapHeight = Math.ceil(Math.max(filterHeight * 1.25, displayObject.__cacheBitmapData.height));
|
||||
needRender = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
bitmapWidth = displayObject.__cacheBitmapData.width;
|
||||
bitmapHeight = displayObject.__cacheBitmapData.height;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bitmapWidth = filterWidth;
|
||||
bitmapHeight = filterHeight;
|
||||
}
|
||||
}
|
||||
|
||||
if (needRender)
|
||||
{
|
||||
updateTransform = true;
|
||||
displayObject.__cacheBitmapBackground = displayObject.opaqueBackground;
|
||||
|
||||
if (filterWidth >= 0.5 && filterHeight >= 0.5)
|
||||
{
|
||||
var needsFill = (displayObject.opaqueBackground != null
|
||||
&& (bitmapWidth != filterWidth || bitmapHeight != filterHeight));
|
||||
var fillColor = displayObject.opaqueBackground != null ? (0xFF << 24) | displayObject.opaqueBackground : 0;
|
||||
var bitmapColor = needsFill ? 0 : fillColor;
|
||||
var allowFramebuffer = (renderer.__type == OPENGL);
|
||||
|
||||
if (displayObject.__cacheBitmapData == null
|
||||
|| bitmapWidth > displayObject.__cacheBitmapData.width
|
||||
|| bitmapHeight > displayObject.__cacheBitmapData.height)
|
||||
{
|
||||
displayObject.__cacheBitmapData = new BitmapData(bitmapWidth, bitmapHeight, true, bitmapColor);
|
||||
|
||||
if (displayObject.__cacheBitmap == null) displayObject.__cacheBitmap = new Bitmap();
|
||||
displayObject.__cacheBitmap.__bitmapData = displayObject.__cacheBitmapData;
|
||||
displayObject.__cacheBitmapRenderer = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
displayObject.__cacheBitmapData.__fillRect(displayObject.__cacheBitmapData.rect, bitmapColor, allowFramebuffer);
|
||||
}
|
||||
|
||||
if (needsFill)
|
||||
{
|
||||
rect.setTo(0, 0, filterWidth, filterHeight);
|
||||
displayObject.__cacheBitmapData.__fillRect(rect, fillColor, allowFramebuffer);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ColorTransform.__pool.release(colorTransform);
|
||||
|
||||
displayObject.__cacheBitmap = null;
|
||||
displayObject.__cacheBitmapData = null;
|
||||
displayObject.__cacheBitmapData2 = null;
|
||||
displayObject.__cacheBitmapData3 = null;
|
||||
displayObject.__cacheBitmapRenderer = null;
|
||||
|
||||
if (displayObject.__drawableType == TEXT_FIELD)
|
||||
{
|
||||
var textField:TextField = cast displayObject;
|
||||
if (textField.__cacheBitmap != null)
|
||||
{
|
||||
textField.__cacheBitmap.__renderTransform.tx -= textField.__offsetX * pixelRatio;
|
||||
textField.__cacheBitmap.__renderTransform.ty -= textField.__offsetY * pixelRatio;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Should we retain these longer?
|
||||
|
||||
displayObject.__cacheBitmapData = displayObject.__cacheBitmap.bitmapData;
|
||||
displayObject.__cacheBitmapData2 = null;
|
||||
displayObject.__cacheBitmapData3 = null;
|
||||
}
|
||||
|
||||
if (updateTransform || needRender)
|
||||
{
|
||||
displayObject.__cacheBitmap.__worldTransform.copyFrom(displayObject.__worldTransform);
|
||||
|
||||
if (bitmapMatrix == displayObject.__renderTransform)
|
||||
{
|
||||
displayObject.__cacheBitmap.__renderTransform.identity();
|
||||
displayObject.__cacheBitmap.__renderTransform.scale(1 / pixelRatio, 1 / pixelRatio);
|
||||
displayObject.__cacheBitmap.__renderTransform.tx = displayObject.__renderTransform.tx + offsetX;
|
||||
displayObject.__cacheBitmap.__renderTransform.ty = displayObject.__renderTransform.ty + offsetY;
|
||||
}
|
||||
else
|
||||
{
|
||||
displayObject.__cacheBitmap.__renderTransform.copyFrom(displayObject.__cacheBitmapMatrix);
|
||||
displayObject.__cacheBitmap.__renderTransform.invert();
|
||||
displayObject.__cacheBitmap.__renderTransform.concat(displayObject.__renderTransform);
|
||||
displayObject.__cacheBitmap.__renderTransform.a *= 1 / pixelRatio;
|
||||
displayObject.__cacheBitmap.__renderTransform.d *= 1 / pixelRatio;
|
||||
displayObject.__cacheBitmap.__renderTransform.tx += offsetX;
|
||||
displayObject.__cacheBitmap.__renderTransform.ty += offsetY;
|
||||
}
|
||||
}
|
||||
|
||||
displayObject.__cacheBitmap.smoothing = renderer.__allowSmoothing;
|
||||
displayObject.__cacheBitmap.__renderable = displayObject.__renderable;
|
||||
displayObject.__cacheBitmap.__worldAlpha = displayObject.__worldAlpha;
|
||||
displayObject.__cacheBitmap.__worldBlendMode = displayObject.__worldBlendMode;
|
||||
displayObject.__cacheBitmap.__worldShader = displayObject.__worldShader;
|
||||
// displayObject.__cacheBitmap.__scrollRect = displayObject.__scrollRect;
|
||||
// displayObject.__cacheBitmap.filters = displayObject.filters;
|
||||
displayObject.__cacheBitmap.mask = displayObject.__mask;
|
||||
|
||||
if (needRender)
|
||||
{
|
||||
#if lime
|
||||
if (displayObject.__cacheBitmapRenderer == null || renderType != displayObject.__cacheBitmapRenderer.__type)
|
||||
{
|
||||
if (renderType == OPENGL)
|
||||
{
|
||||
displayObject.__cacheBitmapRenderer = new OpenGLRenderer(cast(renderer, OpenGLRenderer).__context3D, displayObject.__cacheBitmapData);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (displayObject.__cacheBitmapData.image == null)
|
||||
{
|
||||
var color = displayObject.opaqueBackground != null ? (0xFF << 24) | displayObject.opaqueBackground : 0;
|
||||
displayObject.__cacheBitmapData = new BitmapData(bitmapWidth, bitmapHeight, true, color);
|
||||
displayObject.__cacheBitmap.__bitmapData = displayObject.__cacheBitmapData;
|
||||
}
|
||||
|
||||
#if (js && html5)
|
||||
ImageCanvasUtil.convertToCanvas(displayObject.__cacheBitmapData.image);
|
||||
displayObject.__cacheBitmapRenderer = new CanvasRenderer(displayObject.__cacheBitmapData.image.buffer.__srcContext);
|
||||
#else
|
||||
displayObject.__cacheBitmapRenderer = new CairoRenderer(new Cairo(displayObject.__cacheBitmapData.getSurface()));
|
||||
#end
|
||||
}
|
||||
|
||||
displayObject.__cacheBitmapRenderer.__worldTransform = new Matrix();
|
||||
displayObject.__cacheBitmapRenderer.__worldColorTransform = new ColorTransform();
|
||||
}
|
||||
#else
|
||||
return false;
|
||||
#end
|
||||
|
||||
if (displayObject.__cacheBitmapColorTransform == null) displayObject.__cacheBitmapColorTransform = new ColorTransform();
|
||||
|
||||
displayObject.__cacheBitmapRenderer.__stage = displayObject.stage;
|
||||
|
||||
displayObject.__cacheBitmapRenderer.__allowSmoothing = renderer.__allowSmoothing;
|
||||
displayObject.__cacheBitmapRenderer.__setBlendMode(NORMAL);
|
||||
displayObject.__cacheBitmapRenderer.__worldAlpha = 1 / displayObject.__worldAlpha;
|
||||
|
||||
displayObject.__cacheBitmapRenderer.__worldTransform.copyFrom(displayObject.__renderTransform);
|
||||
displayObject.__cacheBitmapRenderer.__worldTransform.invert();
|
||||
displayObject.__cacheBitmapRenderer.__worldTransform.concat(displayObject.__cacheBitmapMatrix);
|
||||
displayObject.__cacheBitmapRenderer.__worldTransform.tx -= offsetX;
|
||||
displayObject.__cacheBitmapRenderer.__worldTransform.ty -= offsetY;
|
||||
displayObject.__cacheBitmapRenderer.__worldTransform.scale(pixelRatio, pixelRatio);
|
||||
|
||||
displayObject.__cacheBitmapRenderer.__pixelRatio = pixelRatio;
|
||||
|
||||
displayObject.__cacheBitmapRenderer.__worldColorTransform.__copyFrom(colorTransform);
|
||||
displayObject.__cacheBitmapRenderer.__worldColorTransform.__invert();
|
||||
|
||||
displayObject.__isCacheBitmapRender = true;
|
||||
|
||||
if (displayObject.__cacheBitmapRenderer.__type == OPENGL)
|
||||
{
|
||||
var parentRenderer:OpenGLRenderer = cast renderer;
|
||||
var childRenderer:OpenGLRenderer = cast displayObject.__cacheBitmapRenderer;
|
||||
|
||||
var context = childRenderer.__context3D;
|
||||
|
||||
var cacheRTT = context.__state.renderToTexture;
|
||||
var cacheRTTDepthStencil = context.__state.renderToTextureDepthStencil;
|
||||
var cacheRTTAntiAlias = context.__state.renderToTextureAntiAlias;
|
||||
var cacheRTTSurfaceSelector = context.__state.renderToTextureSurfaceSelector;
|
||||
|
||||
// var cacheFramebuffer = context.__contextState.__currentGLFramebuffer;
|
||||
|
||||
var cacheBlendMode = parentRenderer.__blendMode;
|
||||
parentRenderer.__suspendClipAndMask();
|
||||
childRenderer.__copyShader(parentRenderer);
|
||||
// childRenderer.__copyState (parentRenderer);
|
||||
|
||||
displayObject.__cacheBitmapData.__setUVRect(context, 0, 0, filterWidth, filterHeight);
|
||||
childRenderer.__setRenderTarget(displayObject.__cacheBitmapData);
|
||||
if (displayObject.__cacheBitmapData.image != null)
|
||||
displayObject.__cacheBitmapData.__textureVersion = displayObject.__cacheBitmapData.image.version
|
||||
+ 1;
|
||||
|
||||
displayObject.__cacheBitmapData.__drawGL(displayObject, childRenderer);
|
||||
|
||||
if (hasFilters)
|
||||
{
|
||||
var needSecondBitmapData = true;
|
||||
var needCopyOfOriginal = false;
|
||||
|
||||
for (filter in displayObject.__filters)
|
||||
{
|
||||
// if (filter.__needSecondBitmapData) {
|
||||
// needSecondBitmapData = true;
|
||||
// }
|
||||
if (filter.__preserveObject)
|
||||
{
|
||||
needCopyOfOriginal = true;
|
||||
}
|
||||
}
|
||||
|
||||
var bitmap = displayObject.__cacheBitmapData;
|
||||
var bitmap2 = null;
|
||||
var bitmap3 = null;
|
||||
|
||||
// if (needSecondBitmapData) {
|
||||
if (displayObject.__cacheBitmapData2 == null
|
||||
|| bitmapWidth > displayObject.__cacheBitmapData2.width
|
||||
|| bitmapHeight > displayObject.__cacheBitmapData2.height)
|
||||
{
|
||||
displayObject.__cacheBitmapData2 = new BitmapData(bitmapWidth, bitmapHeight, true, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
displayObject.__cacheBitmapData2.fillRect(displayObject.__cacheBitmapData2.rect, 0);
|
||||
if (displayObject.__cacheBitmapData2.image != null)
|
||||
{
|
||||
displayObject.__cacheBitmapData2.__textureVersion = displayObject.__cacheBitmapData2.image.version + 1;
|
||||
}
|
||||
}
|
||||
displayObject.__cacheBitmapData2.__setUVRect(context, 0, 0, filterWidth, filterHeight);
|
||||
bitmap2 = displayObject.__cacheBitmapData2;
|
||||
// } else {
|
||||
// bitmap2 = bitmapData;
|
||||
// }
|
||||
|
||||
if (needCopyOfOriginal)
|
||||
{
|
||||
if (displayObject.__cacheBitmapData3 == null
|
||||
|| bitmapWidth > displayObject.__cacheBitmapData3.width
|
||||
|| bitmapHeight > displayObject.__cacheBitmapData3.height)
|
||||
{
|
||||
displayObject.__cacheBitmapData3 = new BitmapData(bitmapWidth, bitmapHeight, true, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
displayObject.__cacheBitmapData3.fillRect(displayObject.__cacheBitmapData3.rect, 0);
|
||||
if (displayObject.__cacheBitmapData3.image != null)
|
||||
{
|
||||
displayObject.__cacheBitmapData3.__textureVersion = displayObject.__cacheBitmapData3.image.version + 1;
|
||||
}
|
||||
}
|
||||
displayObject.__cacheBitmapData3.__setUVRect(context, 0, 0, filterWidth, filterHeight);
|
||||
bitmap3 = displayObject.__cacheBitmapData3;
|
||||
}
|
||||
|
||||
childRenderer.__setBlendMode(NORMAL);
|
||||
childRenderer.__worldAlpha = 1;
|
||||
childRenderer.__worldTransform.identity();
|
||||
childRenderer.__worldColorTransform.__identity();
|
||||
|
||||
// var sourceRect = bitmap.rect;
|
||||
// if (__tempPoint == null) __tempPoint = new Point ();
|
||||
// var destPoint = __tempPoint;
|
||||
var shader, cacheBitmap;
|
||||
|
||||
for (filter in displayObject.__filters)
|
||||
{
|
||||
if (filter.__preserveObject)
|
||||
{
|
||||
childRenderer.__setRenderTarget(bitmap3);
|
||||
childRenderer.__renderFilterPass(bitmap, childRenderer.__defaultDisplayShader, filter.__smooth);
|
||||
}
|
||||
|
||||
for (i in 0...filter.__numShaderPasses)
|
||||
{
|
||||
shader = filter.__initShader(childRenderer, i, filter.__preserveObject ? bitmap3 : null);
|
||||
childRenderer.__setBlendMode(filter.__shaderBlendMode);
|
||||
childRenderer.__setRenderTarget(bitmap2);
|
||||
childRenderer.__renderFilterPass(bitmap, shader, filter.__smooth);
|
||||
|
||||
cacheBitmap = bitmap;
|
||||
bitmap = bitmap2;
|
||||
bitmap2 = cacheBitmap;
|
||||
}
|
||||
|
||||
filter.__renderDirty = false;
|
||||
}
|
||||
|
||||
displayObject.__cacheBitmap.__bitmapData = bitmap;
|
||||
}
|
||||
|
||||
parentRenderer.__blendMode = NORMAL;
|
||||
parentRenderer.__setBlendMode(cacheBlendMode);
|
||||
parentRenderer.__copyShader(childRenderer);
|
||||
|
||||
if (cacheRTT != null)
|
||||
{
|
||||
context.setRenderToTexture(cacheRTT, cacheRTTDepthStencil, cacheRTTAntiAlias, cacheRTTSurfaceSelector);
|
||||
}
|
||||
else
|
||||
{
|
||||
context.setRenderToBackBuffer();
|
||||
}
|
||||
|
||||
// context.__bindGLFramebuffer (cacheFramebuffer);
|
||||
|
||||
// parentRenderer.__restoreState (childRenderer);
|
||||
parentRenderer.__resumeClipAndMask(childRenderer);
|
||||
parentRenderer.setViewport();
|
||||
|
||||
displayObject.__cacheBitmapColorTransform.__copyFrom(colorTransform);
|
||||
}
|
||||
else
|
||||
{
|
||||
#if (js && html5)
|
||||
displayObject.__cacheBitmapData.__drawCanvas(displayObject, cast displayObject.__cacheBitmapRenderer);
|
||||
#else
|
||||
displayObject.__cacheBitmapData.__drawCairo(displayObject, cast displayObject.__cacheBitmapRenderer);
|
||||
#end
|
||||
|
||||
if (hasFilters)
|
||||
{
|
||||
var needSecondBitmapData = false;
|
||||
var needCopyOfOriginal = false;
|
||||
|
||||
for (filter in displayObject.__filters)
|
||||
{
|
||||
if (filter.__needSecondBitmapData)
|
||||
{
|
||||
needSecondBitmapData = true;
|
||||
}
|
||||
if (filter.__preserveObject)
|
||||
{
|
||||
needCopyOfOriginal = true;
|
||||
}
|
||||
}
|
||||
|
||||
var bitmap = displayObject.__cacheBitmapData;
|
||||
var bitmap2 = null;
|
||||
var bitmap3 = null;
|
||||
|
||||
if (needSecondBitmapData)
|
||||
{
|
||||
if (displayObject.__cacheBitmapData2 == null
|
||||
|| displayObject.__cacheBitmapData2.image == null
|
||||
|| bitmapWidth > displayObject.__cacheBitmapData2.width
|
||||
|| bitmapHeight > displayObject.__cacheBitmapData2.height)
|
||||
{
|
||||
displayObject.__cacheBitmapData2 = new BitmapData(bitmapWidth, bitmapHeight, true, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
displayObject.__cacheBitmapData2.fillRect(displayObject.__cacheBitmapData2.rect, 0);
|
||||
}
|
||||
bitmap2 = displayObject.__cacheBitmapData2;
|
||||
}
|
||||
else
|
||||
{
|
||||
bitmap2 = bitmap;
|
||||
}
|
||||
|
||||
if (needCopyOfOriginal)
|
||||
{
|
||||
if (displayObject.__cacheBitmapData3 == null
|
||||
|| displayObject.__cacheBitmapData3.image == null
|
||||
|| bitmapWidth > displayObject.__cacheBitmapData3.width
|
||||
|| bitmapHeight > displayObject.__cacheBitmapData3.height)
|
||||
{
|
||||
displayObject.__cacheBitmapData3 = new BitmapData(bitmapWidth, bitmapHeight, true, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
displayObject.__cacheBitmapData3.fillRect(displayObject.__cacheBitmapData3.rect, 0);
|
||||
}
|
||||
bitmap3 = displayObject.__cacheBitmapData3;
|
||||
}
|
||||
|
||||
if (displayObject.__tempPoint == null) displayObject.__tempPoint = new Point();
|
||||
var destPoint = displayObject.__tempPoint;
|
||||
var cacheBitmap, lastBitmap;
|
||||
|
||||
for (filter in displayObject.__filters)
|
||||
{
|
||||
if (filter.__preserveObject)
|
||||
{
|
||||
bitmap3.copyPixels(bitmap, bitmap.rect, destPoint);
|
||||
}
|
||||
|
||||
lastBitmap = filter.__applyFilter(bitmap2, bitmap, bitmap.rect, destPoint);
|
||||
|
||||
if (filter.__preserveObject)
|
||||
{
|
||||
lastBitmap.draw(bitmap3, null,
|
||||
displayObject.__objectTransform != null ? displayObject.__objectTransform.__colorTransform : null);
|
||||
}
|
||||
filter.__renderDirty = false;
|
||||
|
||||
if (needSecondBitmapData && lastBitmap == bitmap2)
|
||||
{
|
||||
cacheBitmap = bitmap;
|
||||
bitmap = bitmap2;
|
||||
bitmap2 = cacheBitmap;
|
||||
}
|
||||
}
|
||||
|
||||
if (displayObject.__cacheBitmapData != bitmap)
|
||||
{
|
||||
// TODO: Fix issue with swapping __cacheBitmap.__bitmapData
|
||||
// __cacheBitmapData.copyPixels (bitmap, bitmap.rect, destPoint);
|
||||
|
||||
// Adding __cacheBitmapRenderer = null; makes this work
|
||||
cacheBitmap = displayObject.__cacheBitmapData;
|
||||
displayObject.__cacheBitmapData = bitmap;
|
||||
displayObject.__cacheBitmapData2 = cacheBitmap;
|
||||
displayObject.__cacheBitmap.__bitmapData = displayObject.__cacheBitmapData;
|
||||
displayObject.__cacheBitmapRenderer = null;
|
||||
}
|
||||
|
||||
displayObject.__cacheBitmap.__imageVersion = displayObject.__cacheBitmapData.__textureVersion;
|
||||
}
|
||||
|
||||
displayObject.__cacheBitmapColorTransform.__copyFrom(colorTransform);
|
||||
|
||||
if (!displayObject.__cacheBitmapColorTransform.__isDefault(true))
|
||||
{
|
||||
displayObject.__cacheBitmapColorTransform.alphaMultiplier = 1;
|
||||
displayObject.__cacheBitmapData.colorTransform(displayObject.__cacheBitmapData.rect, displayObject.__cacheBitmapColorTransform);
|
||||
}
|
||||
}
|
||||
|
||||
displayObject.__isCacheBitmapRender = false;
|
||||
}
|
||||
|
||||
if (updateTransform || needRender)
|
||||
{
|
||||
Rectangle.__pool.release(rect);
|
||||
}
|
||||
|
||||
updated = updateTransform;
|
||||
}
|
||||
else if (displayObject.__cacheBitmap != null)
|
||||
{
|
||||
if (renderer.__type == DOM)
|
||||
{
|
||||
var domRenderer:DOMRenderer = cast renderer;
|
||||
domRenderer.__renderDrawableClear(displayObject.__cacheBitmap);
|
||||
}
|
||||
|
||||
displayObject.__cacheBitmap = null;
|
||||
displayObject.__cacheBitmapData = null;
|
||||
displayObject.__cacheBitmapData2 = null;
|
||||
displayObject.__cacheBitmapData3 = null;
|
||||
displayObject.__cacheBitmapColorTransform = null;
|
||||
displayObject.__cacheBitmapRenderer = null;
|
||||
|
||||
updated = true;
|
||||
}
|
||||
|
||||
ColorTransform.__pool.release(colorTransform);
|
||||
|
||||
if (updated && displayObject.__drawableType == TEXT_FIELD)
|
||||
{
|
||||
var textField:TextField = cast displayObject;
|
||||
if (textField.__cacheBitmap != null)
|
||||
{
|
||||
textField.__cacheBitmap.__renderTransform.tx -= textField.__offsetX;
|
||||
textField.__cacheBitmap.__renderTransform.ty -= textField.__offsetY;
|
||||
}
|
||||
}
|
||||
|
||||
return updated;
|
||||
#else
|
||||
return false;
|
||||
#end
|
||||
}
|
||||
}
|
||||
#else
|
||||
typedef DisplayObjectRenderer = Dynamic;
|
||||
#end
|
||||
@@ -1,89 +0,0 @@
|
||||
package openfl.display;
|
||||
|
||||
import openfl.utils.ByteArray;
|
||||
|
||||
#if !openfl_debug
|
||||
@:fileXml('tags="haxe,release"')
|
||||
@:noDebug
|
||||
#end
|
||||
class GraphicsShader extends Shader
|
||||
{
|
||||
@:glVertexHeader("attribute float openfl_Alpha;
|
||||
attribute vec4 openfl_ColorMultiplier;
|
||||
attribute vec4 openfl_ColorOffset;
|
||||
attribute vec4 openfl_Position;
|
||||
attribute vec2 openfl_TextureCoord;
|
||||
|
||||
varying float openfl_Alphav;
|
||||
varying vec4 openfl_ColorMultiplierv;
|
||||
varying vec4 openfl_ColorOffsetv;
|
||||
varying vec2 openfl_TextureCoordv;
|
||||
|
||||
uniform mat4 openfl_Matrix;
|
||||
uniform bool openfl_HasColorTransform;
|
||||
uniform vec2 openfl_TextureSize;")
|
||||
@:glVertexBody("openfl_Alphav = openfl_Alpha;
|
||||
openfl_TextureCoordv = openfl_TextureCoord;
|
||||
|
||||
if (openfl_HasColorTransform) {
|
||||
openfl_ColorMultiplierv = openfl_ColorMultiplier;
|
||||
openfl_ColorOffsetv = openfl_ColorOffset / 255.0;
|
||||
}
|
||||
|
||||
gl_Position = openfl_Matrix * openfl_Position;")
|
||||
@:glVertexSource("#pragma header
|
||||
|
||||
void main(void) {
|
||||
#pragma body
|
||||
}")
|
||||
@:glFragmentHeader("varying float openfl_Alphav;
|
||||
varying vec4 openfl_ColorMultiplierv;
|
||||
varying vec4 openfl_ColorOffsetv;
|
||||
varying vec2 openfl_TextureCoordv;
|
||||
|
||||
uniform bool openfl_HasColorTransform;
|
||||
uniform vec2 openfl_TextureSize;
|
||||
uniform sampler2D bitmap;")
|
||||
@:glFragmentBody("vec4 color = texture2D (bitmap, openfl_TextureCoordv);
|
||||
|
||||
if (color.a == 0.0) {
|
||||
gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
} else if (openfl_HasColorTransform) {
|
||||
color = vec4(color.rgb / color.a, color.a);
|
||||
|
||||
mat4 colorMultiplier = mat4(0);
|
||||
colorMultiplier[0][0] = openfl_ColorMultiplierv.x;
|
||||
colorMultiplier[1][1] = openfl_ColorMultiplierv.y;
|
||||
colorMultiplier[2][2] = openfl_ColorMultiplierv.z;
|
||||
colorMultiplier[3][3] = 1.0; // openfl_ColorMultiplierv.w;
|
||||
|
||||
color = clamp(openfl_ColorOffsetv + (color * colorMultiplier), 0.0, 1.0);
|
||||
|
||||
if (color.a > 0.0) {
|
||||
gl_FragColor = vec4(color.rgb * color.a * openfl_Alphav, color.a * openfl_Alphav);
|
||||
} else {
|
||||
gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
}
|
||||
} else {
|
||||
gl_FragColor = color * openfl_Alphav;
|
||||
}")
|
||||
#if emscripten
|
||||
@:glFragmentSource("#pragma header
|
||||
|
||||
void main(void) {
|
||||
#pragma body
|
||||
|
||||
gl_FragColor = gl_FragColor.bgra;
|
||||
}")
|
||||
#else
|
||||
@:glFragmentSource("#pragma header
|
||||
|
||||
void main(void) {
|
||||
#pragma body
|
||||
}")
|
||||
#end
|
||||
public function new(code:ByteArray = null)
|
||||
{
|
||||
super(code);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,95 +0,0 @@
|
||||
package openfl.display3D.utils;
|
||||
|
||||
import flixel.util.FlxPool.IFlxPooled;
|
||||
import flixel.util.FlxPool;
|
||||
import lime.utils.UInt8Array;
|
||||
|
||||
class UInt8Buff implements IFlxPooled
|
||||
{
|
||||
static var _pools:Map<Int, FlxPool<UInt8Buff>> = [];
|
||||
|
||||
public static function getPool(length:Int) {
|
||||
if(!_pools.exists(length)) {
|
||||
_pools.set(length, new FlxPool<UInt8Buff>(UInt8Buff));
|
||||
}
|
||||
return _pools.get(length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Recycle or create new FlxRect.
|
||||
* Be sure to put() them back into the pool after you're done with them!
|
||||
*/
|
||||
public static inline function get(length:Int):UInt8Buff
|
||||
{
|
||||
var rect = getPool(length).get().set(length);
|
||||
rect._inPool = false;
|
||||
if(rect.buffer == null)
|
||||
rect.buffer = new UInt8Array(length);
|
||||
return rect;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recycle or create a new FlxRect which will automatically be released
|
||||
* to the pool when passed into a flixel function.
|
||||
*/
|
||||
public static inline function weak(length:Int):UInt8Buff
|
||||
{
|
||||
var rect = get(length);
|
||||
rect._weak = true;
|
||||
return rect;
|
||||
}
|
||||
|
||||
var _weak:Bool = false;
|
||||
var _inPool:Bool = false;
|
||||
|
||||
public var length(get, never):Int;
|
||||
|
||||
inline function get_length() {
|
||||
return buffer.length;
|
||||
}
|
||||
|
||||
public var buffer:UInt8Array;
|
||||
|
||||
@:keep
|
||||
private function new(length:Int)
|
||||
{
|
||||
set(length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add this FlxRect to the recycling pool.
|
||||
*/
|
||||
public inline function put():Void
|
||||
{
|
||||
if (!_inPool)
|
||||
{
|
||||
_inPool = true;
|
||||
_weak = false;
|
||||
getPool(length).putUnsafe(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add this FlxPoint to the recycling pool if it's a weak reference (allocated via weak()).
|
||||
*/
|
||||
public inline function putWeak():Void
|
||||
{
|
||||
if (_weak)
|
||||
{
|
||||
put();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Does nothing
|
||||
*/
|
||||
private inline function set(_length:Int):UInt8Buff
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Necessary for IFlxDestroyable.
|
||||
*/
|
||||
public function destroy() {}
|
||||
}
|
||||
@@ -1,705 +0,0 @@
|
||||
package openfl.media;
|
||||
|
||||
#if !flash
|
||||
import haxe.Int64;
|
||||
import openfl.events.Event;
|
||||
import openfl.events.EventDispatcher;
|
||||
import openfl.events.IOErrorEvent;
|
||||
import openfl.net.URLRequest;
|
||||
import openfl.utils.ByteArray;
|
||||
import openfl.utils.Future;
|
||||
#if lime
|
||||
import openfl.utils._internal.UInt8Array;
|
||||
import lime.media.AudioBuffer;
|
||||
import lime.media.AudioSource;
|
||||
#end
|
||||
|
||||
/**
|
||||
The Sound class lets you work with sound in an application. The Sound class
|
||||
lets you create a Sound object, load and play an external MP3 file into
|
||||
that object, close the sound stream, and access data about the sound, such
|
||||
as information about the number of bytes in the stream and ID3 metadata.
|
||||
More detailed control of the sound is performed through the sound source
|
||||
- the SoundChannel or Microphone object for the sound - and through the
|
||||
properties in the SoundTransform class that control the output of the sound
|
||||
to the computer's speakers.
|
||||
|
||||
In Flash Player 10 and later and AIR 1.5 and later, you can also use
|
||||
this class to work with sound that is generated dynamically. In this case,
|
||||
the Sound object uses the function you assign to a `sampleData`
|
||||
event handler to poll for sound data. The sound is played as it is
|
||||
retrieved from a ByteArray object that you populate with sound data. You
|
||||
can use `Sound.extract()` to extract sound data from a Sound
|
||||
object, after which you can manipulate it before writing it back to the
|
||||
stream for playback.
|
||||
|
||||
To control sounds that are embedded in a SWF file, use the properties in
|
||||
the SoundMixer class.
|
||||
|
||||
**Note**: The ActionScript 3.0 Sound API differs from ActionScript
|
||||
2.0. In ActionScript 3.0, you cannot take sound objects and arrange them in
|
||||
a hierarchy to control their properties.
|
||||
|
||||
When you use this class, consider the following security model:
|
||||
|
||||
|
||||
* Loading and playing a sound is not allowed if the calling file is in
|
||||
a network sandbox and the sound file to be loaded is local.
|
||||
* By default, loading and playing a sound is not allowed if the calling
|
||||
file is local and tries to load and play a remote sound. A user must grant
|
||||
explicit permission to allow this type of access.
|
||||
* Certain operations dealing with sound are restricted. The data in a
|
||||
loaded sound cannot be accessed by a file in a different domain unless you
|
||||
implement a cross-domain policy file. Sound-related APIs that fall under
|
||||
this restriction are `Sound.id3`,
|
||||
`SoundMixer.computeSpectrum()`,
|
||||
`SoundMixer.bufferTime`, and the `SoundTransform`
|
||||
class.
|
||||
|
||||
|
||||
However, in Adobe AIR, content in the `application` security
|
||||
sandbox(content installed with the AIR application) are not restricted by
|
||||
these security limitations.
|
||||
|
||||
For more information related to security, see the Flash Player Developer
|
||||
Center Topic: [Security](http://www.adobe.com/go/devnet_security_en).
|
||||
|
||||
@event complete Dispatched when data has loaded successfully.
|
||||
@event id3 Dispatched by a Sound object when ID3 data is available
|
||||
for an MP3 sound.
|
||||
@event ioError Dispatched when an input/output error occurs that causes
|
||||
a load operation to fail.
|
||||
@event open Dispatched when a load operation starts.
|
||||
@event progress Dispatched when data is received as a load operation
|
||||
progresses.
|
||||
@event sampleData Dispatched when the runtime requests new audio data.
|
||||
**/
|
||||
#if !openfl_debug
|
||||
@:fileXml('tags="haxe,release"')
|
||||
@:noDebug
|
||||
#end
|
||||
@:access(lime.media.AudioBuffer)
|
||||
@:access(lime.utils.AssetLibrary)
|
||||
@:access(openfl.media.SoundMixer)
|
||||
@:access(openfl.media.SoundChannel.new)
|
||||
@:autoBuild(openfl.utils._internal.AssetsMacro.embedSound())
|
||||
class Sound extends EventDispatcher
|
||||
{
|
||||
/**
|
||||
Returns the currently available number of bytes in this sound object. This
|
||||
property is usually useful only for externally loaded files.
|
||||
**/
|
||||
public var bytesLoaded(default, null):Int;
|
||||
|
||||
/**
|
||||
Returns the total number of bytes in this sound object.
|
||||
**/
|
||||
public var bytesTotal(default, null):Int;
|
||||
|
||||
/**
|
||||
Provides access to the metadata that is part of an MP3 file.
|
||||
MP3 sound files can contain ID3 tags, which provide metadata about the
|
||||
file. If an MP3 sound that you load using the `Sound.load()` method
|
||||
contains ID3 tags, you can query these properties. Only ID3 tags that
|
||||
use the UTF-8 character set are supported.
|
||||
|
||||
Flash Player 9 and later and AIR support ID3 2.0 tags, specifically
|
||||
2.3 and 2.4. The following tables list the standard ID3 2.0 tags and
|
||||
the type of content the tags represent. The `Sound.id3` property
|
||||
provides access to these tags through the format `my_sound.id3.COMM`,
|
||||
`my_sound.id3.TIME`, and so on. The first table describes tags that
|
||||
can be accessed either through the ID3 2.0 property name or the
|
||||
ActionScript property name. The second table describes ID3 tags that
|
||||
are supported but do not have predefined properties in ActionScript.
|
||||
|
||||
| ID3 2.0 tag | Corresponding Sound class property |
|
||||
| --- | --- |
|
||||
| COMM | Sound.id3.comment |
|
||||
| TALB | Sound.id3.album |
|
||||
| TCON | Sound.id3.genre |
|
||||
| TIT2 | Sound.id3.songName |
|
||||
| TPE1 | Sound.id3.artist |
|
||||
| TRCK | Sound.id3.track |
|
||||
| TYER | Sound.id3.year |
|
||||
|
||||
The following table describes ID3 tags that are supported but do not
|
||||
have predefined properties in the Sound class. You access them by
|
||||
calling `mySound.id3.TFLT`, `mySound.id3.TIME`, and so on. **NOTE:**
|
||||
None of these tags are supported in Flash Lite 4.
|
||||
|
||||
| Property | Description |
|
||||
| --- | --- |
|
||||
| TFLT | File type |
|
||||
| TIME | Time |
|
||||
| TIT1 | Content group description |
|
||||
| TIT2 | Title/song name/content description |
|
||||
| TIT3 | Subtitle/description refinement |
|
||||
| TKEY | Initial key |
|
||||
| TLAN | Languages |
|
||||
| TLEN | Length |
|
||||
| TMED | Media type |
|
||||
| TOAL | Original album/movie/show title |
|
||||
| TOFN | Original filename |
|
||||
| TOLY | Original lyricists/text writers |
|
||||
| TOPE | Original artists/performers |
|
||||
| TORY | Original release year |
|
||||
| TOWN | File owner/licensee |
|
||||
| TPE1 | Lead performers/soloists |
|
||||
| TPE2 | Band/orchestra/accompaniment |
|
||||
| TPE3 | Conductor/performer refinement |
|
||||
| TPE4 | Interpreted, remixed, or otherwise modified by |
|
||||
| TPOS | Part of a set |
|
||||
| TPUB | Publisher |
|
||||
| TRCK | Track number/position in set |
|
||||
| TRDA | Recording dates |
|
||||
| TRSN | Internet radio station name |
|
||||
| TRSO | Internet radio station owner |
|
||||
| TSIZ | Size |
|
||||
| TSRC | ISRC (international standard recording code) |
|
||||
| TSSE | Software/hardware and settings used for encoding |
|
||||
| TYER | Year |
|
||||
| WXXX | URL link frame |
|
||||
|
||||
When using this property, consider the Flash Player security model:
|
||||
|
||||
* The `id3` property of a Sound object is always permitted for SWF
|
||||
files that are in the same security sandbox as the sound file. For
|
||||
files in other sandboxes, there are security checks.
|
||||
* When you load the sound, using the `load()` method of the Sound
|
||||
class, you can specify a `context` parameter, which is a
|
||||
SoundLoaderContext object. If you set the `checkPolicyFile` property
|
||||
of the SoundLoaderContext object to `true`, Flash Player checks for a
|
||||
URL policy file on the server from which the sound is loaded. If a
|
||||
policy file exists and permits access from the domain of the loading
|
||||
SWF file, then the file is allowed to access the `id3` property of the
|
||||
Sound object; otherwise it is not.
|
||||
|
||||
However, in Adobe AIR, content in the `application` security sandbox
|
||||
(content installed with the AIR application) are not restricted by
|
||||
these security limitations.
|
||||
|
||||
For more information related to security, see the Flash Player
|
||||
Developer Center Topic: <a
|
||||
href="http://www.adobe.com/go/devnet_security_en"
|
||||
scope="external">Security</a>.
|
||||
**/
|
||||
public var id3(get, never):ID3Info;
|
||||
|
||||
/**
|
||||
Returns the buffering state of external MP3 files. If the value is
|
||||
`true`, any playback is currently suspended while the object
|
||||
waits for more data.
|
||||
**/
|
||||
public var isBuffering(default, null):Bool;
|
||||
|
||||
// @:noCompletion @:dox(hide) @:require(flash10_1) public var isURLInaccessible (default, null):Bool;
|
||||
|
||||
/**
|
||||
The length of the current sound in milliseconds.
|
||||
**/
|
||||
public var length(get, never):Float;
|
||||
|
||||
/**
|
||||
The URL from which this sound was loaded. This property is applicable only
|
||||
to Sound objects that were loaded using the `Sound.load()`
|
||||
method. For Sound objects that are associated with a sound asset from a
|
||||
SWF file's library, the value of the `url` property is
|
||||
`null`.
|
||||
|
||||
When you first call `Sound.load()`, the `url`
|
||||
property initially has a value of `null`, because the final URL
|
||||
is not yet known. The `url` property will have a non-null value
|
||||
as soon as an `open` event is dispatched from the Sound
|
||||
object.
|
||||
|
||||
The `url` property contains the final, absolute URL from
|
||||
which a sound was loaded. The value of `url` is usually the
|
||||
same as the value passed to the `stream` parameter of
|
||||
`Sound.load()`. However, if you passed a relative URL to
|
||||
`Sound.load()` the value of the `url` property
|
||||
represents the absolute URL. Additionally, if the original URL request is
|
||||
redirected by an HTTP server, the value of the `url` property
|
||||
reflects the final URL from which the sound file was actually downloaded.
|
||||
This reporting of an absolute, final URL is equivalent to the behavior of
|
||||
`LoaderInfo.url`.
|
||||
|
||||
In some cases, the value of the `url` property is truncated;
|
||||
see the `isURLInaccessible` property for details.
|
||||
**/
|
||||
public var url(default, null):String;
|
||||
|
||||
#if lime
|
||||
@:noCompletion private var __buffer:AudioBuffer;
|
||||
#end
|
||||
|
||||
#if openfljs
|
||||
@:noCompletion private static function __init__()
|
||||
{
|
||||
untyped Object.defineProperties(Sound.prototype, {
|
||||
"id3": {get: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function () { return this.get_id3 (); }")},
|
||||
"length": {get: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function () { return this.get_length (); }")},
|
||||
});
|
||||
}
|
||||
#end
|
||||
|
||||
/**
|
||||
Creates a new Sound object. If you pass a valid URLRequest object to the
|
||||
Sound constructor, the constructor automatically calls the
|
||||
`load()` function for the Sound object. If you do not pass a
|
||||
valid URLRequest object to the Sound constructor, you must call the
|
||||
`load()` function for the Sound object yourself, or the stream
|
||||
will not load.
|
||||
|
||||
Once `load()` is called on a Sound object, you can't later
|
||||
load a different sound file into that Sound object. To load a different
|
||||
sound file, create a new Sound object.
|
||||
In Flash Player 10 and later and AIR 1.5 and later, instead of using
|
||||
`load()`, you can use the `sampleData` event handler
|
||||
to load sound dynamically into the Sound object.
|
||||
|
||||
@param stream The URL that points to an external MP3 file.
|
||||
@param context An optional SoundLoader context object, which can define
|
||||
the buffer time(the minimum number of milliseconds of MP3
|
||||
data to hold in the Sound object's buffer) and can specify
|
||||
whether the application should check for a cross-domain
|
||||
policy file prior to loading the sound.
|
||||
**/
|
||||
public function new(stream:URLRequest = null, context:SoundLoaderContext = null)
|
||||
{
|
||||
super(this);
|
||||
|
||||
bytesLoaded = 0;
|
||||
bytesTotal = 0;
|
||||
isBuffering = false;
|
||||
url = null;
|
||||
|
||||
if (stream != null)
|
||||
{
|
||||
load(stream, context);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Closes the stream, causing any download of data to cease. No data may
|
||||
be read from the stream after the `close()` method is called.
|
||||
|
||||
@throws IOError The stream could not be closed, or the stream was not
|
||||
open.
|
||||
**/
|
||||
public function close():Void
|
||||
{
|
||||
#if lime
|
||||
if (__buffer != null)
|
||||
{
|
||||
__buffer.dispose();
|
||||
__buffer = null;
|
||||
}
|
||||
#end
|
||||
}
|
||||
|
||||
#if false
|
||||
/**
|
||||
Extracts raw sound data from a Sound object.
|
||||
This method is designed to be used when you are working with
|
||||
dynamically generated audio, using a function you assign to the
|
||||
`sampleData` event for a different Sound object. That is, you can use
|
||||
this method to extract sound data from a Sound object. Then you can
|
||||
write the data to the byte array that another Sound object is using to
|
||||
stream dynamic audio.
|
||||
|
||||
The audio data is placed in the target byte array starting from the
|
||||
current position of the byte array. The audio data is always exposed
|
||||
as 44100 Hz Stereo. The sample type is a 32-bit floating-point value,
|
||||
which can be converted to a Number using `ByteArray.readFloat()`.
|
||||
|
||||
@param target A ByteArray object in which the extracted sound samples
|
||||
are placed.
|
||||
@param length The number of sound samples to extract. A sample
|
||||
contains both the left and right channels נthat is,
|
||||
two 32-bit floating-point values.
|
||||
@return The number of samples written to the ByteArray specified in
|
||||
the `target` parameter.
|
||||
**/
|
||||
// @:noCompletion @:dox(hide) @:require(flash10) public function extract (target:ByteArray, length:Float, startPosition:Float = -1):Float;
|
||||
#end
|
||||
|
||||
#if lime
|
||||
/**
|
||||
Creates a new Sound from an AudioBuffer immediately.
|
||||
|
||||
@param buffer An AudioBuffer instance
|
||||
@returns A new Sound
|
||||
**/
|
||||
public static function fromAudioBuffer(buffer:AudioBuffer):Sound
|
||||
{
|
||||
var sound = new Sound();
|
||||
sound.__buffer = buffer;
|
||||
return sound;
|
||||
}
|
||||
#end
|
||||
|
||||
/**
|
||||
Creates a new Sound from a file path synchronously. This means that the
|
||||
Sound will be returned immediately (if supported).
|
||||
|
||||
HTML5 and Flash do not support creating Sound synchronously, so these targets
|
||||
always return `null`.
|
||||
|
||||
In order to load files from a remote web address, use the `loadFromFile` method,
|
||||
which supports asynchronous loading.
|
||||
|
||||
@param path A local file path containing a sound
|
||||
@returns A new Sound if successful, or `null` if unsuccessful
|
||||
**/
|
||||
public static function fromFile(path:String):Sound
|
||||
{
|
||||
#if lime
|
||||
return fromAudioBuffer(AudioBuffer.fromFile(path));
|
||||
#else
|
||||
return null;
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
Initiates loading of an external MP3 file from the specified URL. If you
|
||||
provide a valid URLRequest object to the Sound constructor, the
|
||||
constructor calls `Sound.load()` for you. You only need to call
|
||||
`Sound.load()` yourself if you don't pass a valid URLRequest
|
||||
object to the Sound constructor or you pass a `null` value.
|
||||
|
||||
Once `load()` is called on a Sound object, you can't later
|
||||
load a different sound file into that Sound object. To load a different
|
||||
sound file, create a new Sound object.
|
||||
|
||||
When using this method, consider the following security model:
|
||||
|
||||
* Calling `Sound.load()` is not allowed if the calling file
|
||||
is in the local-with-file-system sandbox and the sound is in a network
|
||||
sandbox.
|
||||
* Access from the local-trusted or local-with-networking sandbox
|
||||
requires permission from a website through a URL policy file.
|
||||
* You cannot connect to commonly reserved ports. For a complete list
|
||||
of blocked ports, see "Restricting Networking APIs" in the _ActionScript
|
||||
3.0 Developer's Guide_.
|
||||
* You can prevent a SWF file from using this method by setting the
|
||||
`allowNetworking` parameter of the `object` and
|
||||
`embed` tags in the HTML page that contains the SWF
|
||||
content.
|
||||
|
||||
In Flash Player 10 and later, if you use a multipart Content-Type(for
|
||||
example "multipart/form-data") that contains an upload(indicated by a
|
||||
"filename" parameter in a "content-disposition" header within the POST
|
||||
body), the POST operation is subject to the security rules applied to
|
||||
uploads:
|
||||
|
||||
* The POST operation must be performed in response to a user-initiated
|
||||
action, such as a mouse click or key press.
|
||||
* If the POST operation is cross-domain(the POST target is not on the
|
||||
same server as the SWF file that is sending the POST request), the target
|
||||
server must provide a URL policy file that permits cross-domain
|
||||
access.
|
||||
|
||||
|
||||
Also, for any multipart Content-Type, the syntax must be valid
|
||||
(according to the RFC2046 standards). If the syntax appears to be invalid,
|
||||
the POST operation is subject to the security rules applied to
|
||||
uploads.
|
||||
|
||||
In Adobe AIR, content in the `application` security sandbox
|
||||
(content installed with the AIR application) are not restricted by these
|
||||
security limitations.
|
||||
|
||||
For more information related to security, see the Flash Player
|
||||
Developer Center Topic: [Security](http://www.adobe.com/go/devnet_security_en).
|
||||
|
||||
@param stream A URL that points to an external MP3 file.
|
||||
@param context An optional SoundLoader context object, which can define
|
||||
the buffer time(the minimum number of milliseconds of MP3
|
||||
data to hold in the Sound object's buffer) and can specify
|
||||
whether the application should check for a cross-domain
|
||||
policy file prior to loading the sound.
|
||||
@throws IOError A network error caused the load to fail.
|
||||
@throws IOError The `digest` property of the
|
||||
`stream` object is not `null`.
|
||||
You should only set the `digest` property
|
||||
of a URLRequest object when calling the
|
||||
`URLLoader.load()` method when loading a
|
||||
SWZ file(an Adobe platform component).
|
||||
@throws SecurityError Local untrusted files may not communicate with the
|
||||
Internet. You can work around this by reclassifying
|
||||
this file as local-with-networking or trusted.
|
||||
@throws SecurityError You cannot connect to commonly reserved ports. For a
|
||||
complete list of blocked ports, see "Restricting
|
||||
Networking APIs" in the _ActionScript 3.0
|
||||
Developer's Guide_.
|
||||
**/
|
||||
public function load(stream:URLRequest, context:SoundLoaderContext = null):Void
|
||||
{
|
||||
url = stream.url;
|
||||
|
||||
#if lime
|
||||
#if (js && html5)
|
||||
var defaultLibrary = lime.utils.Assets.getLibrary("default");
|
||||
|
||||
if (defaultLibrary != null && defaultLibrary.cachedAudioBuffers.exists(url))
|
||||
{
|
||||
AudioBuffer_onURLLoad(defaultLibrary.cachedAudioBuffers.get(url));
|
||||
}
|
||||
else
|
||||
{
|
||||
AudioBuffer.loadFromFile(url).onComplete(AudioBuffer_onURLLoad).onError(function(_)
|
||||
{
|
||||
AudioBuffer_onURLLoad(null);
|
||||
});
|
||||
}
|
||||
#else
|
||||
AudioBuffer.loadFromFile(url).onComplete(AudioBuffer_onURLLoad).onError(function(_)
|
||||
{
|
||||
AudioBuffer_onURLLoad(null);
|
||||
});
|
||||
#end
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
Load MP3 sound data from a ByteArray object into a Sound object. The data will be read from the current
|
||||
ByteArray position and will leave the ByteArray position at the end of the specified bytes length once
|
||||
finished. If the MP3 sound data contains ID3 data ID3 events will be dispatched during this function call.
|
||||
This function will throw an exception if the ByteArray object does not contain enough data.
|
||||
|
||||
@param bytes
|
||||
@param bytesLength
|
||||
**/
|
||||
public function loadCompressedDataFromByteArray(bytes:ByteArray, bytesLength:Int):Void
|
||||
{
|
||||
if (bytes == null || bytesLength <= 0)
|
||||
{
|
||||
dispatchEvent(new IOErrorEvent(IOErrorEvent.IO_ERROR));
|
||||
return;
|
||||
}
|
||||
|
||||
if (bytes.position > 0 || bytes.length > bytesLength)
|
||||
{
|
||||
var copy = new ByteArray(bytesLength);
|
||||
copy.writeBytes(bytes, bytes.position, bytesLength);
|
||||
bytes = copy;
|
||||
}
|
||||
|
||||
#if lime
|
||||
__buffer = AudioBuffer.fromBytes(bytes);
|
||||
|
||||
if (__buffer == null)
|
||||
{
|
||||
dispatchEvent(new IOErrorEvent(IOErrorEvent.IO_ERROR));
|
||||
}
|
||||
else
|
||||
{
|
||||
dispatchEvent(new Event(Event.COMPLETE));
|
||||
}
|
||||
#else
|
||||
dispatchEvent(new IOErrorEvent(IOErrorEvent.IO_ERROR));
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
Creates a new Sound from a file path or web address asynchronously. The file
|
||||
load will occur in the background.
|
||||
|
||||
Progress, completion and error callbacks will be dispatched in the current
|
||||
thread using callbacks attached to a returned Future object.
|
||||
|
||||
@param path A local file path or web address containing a sound
|
||||
@returns A Future Sound
|
||||
**/
|
||||
public static function loadFromFile(path:String):Future<Sound>
|
||||
{
|
||||
#if lime
|
||||
return AudioBuffer.loadFromFile(path).then(function(audioBuffer)
|
||||
{
|
||||
return Future.withValue(fromAudioBuffer(audioBuffer));
|
||||
});
|
||||
#else
|
||||
return cast Future.withError("Cannot load audio file");
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
Creates a new Sound from a set of file paths or web addresses asynchronously.
|
||||
The audio backend will choose the first compatible file format, and will load the file
|
||||
it selects in the background.
|
||||
|
||||
Progress, completion and error callbacks will be dispatched in the current
|
||||
thread using callbacks attached to a returned Future object.
|
||||
|
||||
@param paths A set of local file paths or web addresses containing sound
|
||||
@returns A Future Sound
|
||||
**/
|
||||
public static function loadFromFiles(paths:Array<String>):Future<Sound>
|
||||
{
|
||||
#if lime
|
||||
return AudioBuffer.loadFromFiles(paths).then(function(audioBuffer)
|
||||
{
|
||||
return Future.withValue(fromAudioBuffer(audioBuffer));
|
||||
});
|
||||
#else
|
||||
return cast Future.withError("Cannot load audio files");
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
Load PCM 32-bit floating point sound data from a ByteArray object into a Sound object. The data will be read
|
||||
from the current ByteArray position and will leave the ByteArray position at the end of the specified sample
|
||||
length multiplied by either 1 channel or 2 channels if the stereo flag is set once finished.
|
||||
|
||||
Starting with Flash Player 11.8, the amount of audio data that can be passed to this function is limited. For
|
||||
SWF versions >= 21, this function throws an exception if the amount of audio data passed into this function is
|
||||
more than 1800 seconds. That is, samples / sampleRate should be less than or equal to 1800. For swf versions <
|
||||
21, the runtime fails silently if the amount of audio data passed in is more than 12000 seconds. This is
|
||||
provided only for backward compatibility.
|
||||
|
||||
This function throws an exception if the ByteArray object does not contain enough data.
|
||||
|
||||
@param bytes
|
||||
@param samples
|
||||
@param format
|
||||
@param stereo
|
||||
@param sampleRate
|
||||
**/
|
||||
public function loadPCMFromByteArray(bytes:ByteArray, samples:Int, format:String = "float", stereo:Bool = true, sampleRate:Float = 44100):Void
|
||||
{
|
||||
if (bytes == null)
|
||||
{
|
||||
dispatchEvent(new IOErrorEvent(IOErrorEvent.IO_ERROR));
|
||||
return;
|
||||
}
|
||||
|
||||
var bitsPerSample = (format == "float" ? 32 : 16); // "short"
|
||||
var channels = (stereo ? 2 : 1);
|
||||
var bytesLength = Std.int(samples * channels * (bitsPerSample / 8));
|
||||
|
||||
if (bytes.position > 0 || bytes.length > bytesLength)
|
||||
{
|
||||
var copy = new ByteArray(bytesLength);
|
||||
copy.writeBytes(bytes, bytes.position, bytesLength);
|
||||
bytes = copy;
|
||||
}
|
||||
|
||||
#if lime
|
||||
var audioBuffer = new AudioBuffer();
|
||||
audioBuffer.bitsPerSample = bitsPerSample;
|
||||
audioBuffer.channels = channels;
|
||||
audioBuffer.data = new UInt8Array(bytes);
|
||||
audioBuffer.sampleRate = Std.int(sampleRate);
|
||||
|
||||
__buffer = audioBuffer;
|
||||
|
||||
dispatchEvent(new Event(Event.COMPLETE));
|
||||
#else
|
||||
dispatchEvent(new IOErrorEvent(IOErrorEvent.IO_ERROR));
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
Generates a new SoundChannel object to play back the sound. This method
|
||||
returns a SoundChannel object, which you access to stop the sound and to
|
||||
monitor volume.(To control the volume, panning, and balance, access the
|
||||
SoundTransform object assigned to the sound channel.)
|
||||
|
||||
@param startTime The initial position in milliseconds at which playback
|
||||
should start.
|
||||
@param loops Defines the number of times a sound loops back to the
|
||||
`startTime` value before the sound channel
|
||||
stops playback.
|
||||
@param sndTransform The initial SoundTransform object assigned to the
|
||||
sound channel.
|
||||
@return A SoundChannel object, which you use to control the sound. This
|
||||
method returns `null` if you have no sound card or if
|
||||
you run out of available sound channels. The maximum number of
|
||||
sound channels available at once is 32.
|
||||
**/
|
||||
public function play(startTime:Float = 0.0, loops:Int = 0, sndTransform:SoundTransform = null):SoundChannel
|
||||
{
|
||||
#if lime
|
||||
if (__buffer == null || SoundMixer.__soundChannels.length >= SoundMixer.MAX_ACTIVE_CHANNELS)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (sndTransform == null)
|
||||
{
|
||||
sndTransform = new SoundTransform();
|
||||
}
|
||||
else
|
||||
{
|
||||
sndTransform = sndTransform.clone();
|
||||
}
|
||||
|
||||
var pan = SoundMixer.__soundTransform.pan + sndTransform.pan;
|
||||
|
||||
if (pan > 1) pan = 1;
|
||||
else if (pan < -1) pan = -1;
|
||||
|
||||
var volume = SoundMixer.__soundTransform.volume * sndTransform.volume;
|
||||
|
||||
var source = new AudioSource(__buffer);
|
||||
source.offset = Std.int(startTime);
|
||||
if (loops > 1) source.loops = loops - 1;
|
||||
|
||||
source.gain = volume;
|
||||
|
||||
var position = source.position;
|
||||
position.x = pan;
|
||||
position.z = -1 * Math.sqrt(1 - Math.pow(pan, 2));
|
||||
source.position = position;
|
||||
|
||||
return new SoundChannel(source, sndTransform);
|
||||
#else
|
||||
return null;
|
||||
#end
|
||||
}
|
||||
|
||||
// Get & Set Methods
|
||||
@:noCompletion private function get_id3():ID3Info
|
||||
{
|
||||
return new ID3Info();
|
||||
}
|
||||
|
||||
@:noCompletion private function get_length():Float
|
||||
{
|
||||
#if lime
|
||||
if (__buffer != null)
|
||||
{
|
||||
#if (js && html5 && howlerjs)
|
||||
return __buffer.src.duration() * 1000;
|
||||
#else
|
||||
if (__buffer.data != null) return (__buffer.data.length >> 0) / __buffer.channels / (__buffer.bitsPerSample >> 3) / __buffer.sampleRate * 1000;
|
||||
else if (__buffer.__srcVorbisFile != null) {
|
||||
var x = __buffer.__srcVorbisFile.pcmTotal();
|
||||
return (x.high * 4294967296. + (x.low >> 0)) / __buffer.sampleRate * 1000;
|
||||
}
|
||||
#end
|
||||
}
|
||||
#end
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Event Handlers
|
||||
#if lime
|
||||
@:noCompletion private function AudioBuffer_onURLLoad(buffer:AudioBuffer):Void
|
||||
{
|
||||
if (buffer == null)
|
||||
{
|
||||
dispatchEvent(new IOErrorEvent(IOErrorEvent.IO_ERROR));
|
||||
}
|
||||
else
|
||||
{
|
||||
__buffer = buffer;
|
||||
dispatchEvent(new Event(Event.COMPLETE));
|
||||
}
|
||||
}
|
||||
#end
|
||||
}
|
||||
#else
|
||||
typedef Sound = flash.media.Sound;
|
||||
#end
|
||||
@@ -1,400 +0,0 @@
|
||||
package openfl.media;
|
||||
|
||||
#if !flash
|
||||
import haxe.Int64;
|
||||
|
||||
import openfl.events.Event;
|
||||
import openfl.events.EventDispatcher;
|
||||
#if lime
|
||||
import lime.media.AudioSource;
|
||||
import lime.media.openal.AL;
|
||||
#end
|
||||
|
||||
/**
|
||||
The SoundChannel class controls a sound in an application. Every sound is
|
||||
assigned to a sound channel, and the application can have multiple sound
|
||||
channels that are mixed together. The SoundChannel class contains a
|
||||
`stop()` method, properties for monitoring the amplitude
|
||||
(volume) of the channel, and a property for assigning a SoundTransform
|
||||
object to the channel.
|
||||
|
||||
@event soundComplete Dispatched when a sound has finished playing.
|
||||
|
||||
@see [Playing sounds](https://books.openfl.org/openfl-developers-guide/working-with-sound/playing-sounds.html)
|
||||
@see `openfl.media.Sound`
|
||||
**/
|
||||
#if !openfl_debug
|
||||
@:fileXml('tags="haxe,release"')
|
||||
@:noDebug
|
||||
#end
|
||||
#if lime_cffi
|
||||
@:access(lime._internal.backend.native.NativeAudioSource)
|
||||
@:access(lime.media.AudioSource)
|
||||
#end
|
||||
@:access(openfl.media.Sound)
|
||||
@:access(openfl.media.SoundMixer)
|
||||
@:final @:keep class SoundChannel extends EventDispatcher
|
||||
{
|
||||
/**
|
||||
The current amplitude(volume) of the left channel, from 0(silent) to 1
|
||||
(full amplitude).
|
||||
**/
|
||||
public var leftPeak(get, null):Float;
|
||||
|
||||
/**
|
||||
The current amplitude(volume) of the right channel, from 0(silent) to 1
|
||||
(full amplitude).
|
||||
**/
|
||||
public var rightPeak(get, null):Float;
|
||||
|
||||
/**
|
||||
When the sound is playing, the `position` property indicates in
|
||||
milliseconds the current point that is being played in the sound file.
|
||||
When the sound is stopped or paused, the `position` property
|
||||
indicates the last point that was played in the sound file.
|
||||
|
||||
A common use case is to save the value of the `position`
|
||||
property when the sound is stopped. You can resume the sound later by
|
||||
restarting it from that saved position.
|
||||
|
||||
If the sound is looped, `position` is reset to 0 at the
|
||||
beginning of each loop.
|
||||
**/
|
||||
public var position(get, set):Float;
|
||||
|
||||
/**
|
||||
The SoundTransform object assigned to the sound channel. A SoundTransform
|
||||
object includes properties for setting volume, panning, left speaker
|
||||
assignment, and right speaker assignment.
|
||||
**/
|
||||
public var soundTransform(get, set):SoundTransform;
|
||||
|
||||
/**
|
||||
self explanatory
|
||||
*/
|
||||
public var loopTime(get, set):Float;
|
||||
public var endTime(get, set):Null<Float>;
|
||||
public var pitch(get, set):Float;
|
||||
public var loops(get, set):Int;
|
||||
|
||||
@:noCompletion private var __sound:Sound;
|
||||
@:noCompletion private var __isValid:Bool;
|
||||
@:noCompletion private var __soundTransform:SoundTransform;
|
||||
@:noCompletion private var __lastPeakTime:Float;
|
||||
@:noCompletion private var __leftPeak:Float;
|
||||
@:noCompletion private var __rightPeak:Float;
|
||||
#if lime
|
||||
@:noCompletion private var __source:AudioSource;
|
||||
@:noCompletion private var __audioSource(get, set):AudioSource; // forward??? compatibility??
|
||||
#end
|
||||
|
||||
#if openfljs
|
||||
@:noCompletion private static function __init__()
|
||||
{
|
||||
untyped Object.defineProperties(SoundChannel.prototype, {
|
||||
"position": {
|
||||
get: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function () { return this.get_position (); }"),
|
||||
set: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function (v) { return this.set_position (v); }")
|
||||
},
|
||||
"soundTransform": {
|
||||
get: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function () { return this.get_soundTransform (); }"),
|
||||
set: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function (v) { return this.set_soundTransform (v); }")
|
||||
},
|
||||
});
|
||||
}
|
||||
#end
|
||||
|
||||
@:noCompletion private function new(source:#if lime AudioSource #else Dynamic #end = null, soundTransform:SoundTransform = null):Void
|
||||
{
|
||||
super(this);
|
||||
|
||||
if (soundTransform != null) __soundTransform = soundTransform;
|
||||
else __soundTransform = new SoundTransform();
|
||||
|
||||
__initAudioSource(source);
|
||||
|
||||
SoundMixer.__registerSoundChannel(this);
|
||||
}
|
||||
|
||||
/**
|
||||
Stops the sound playing in the channel.
|
||||
**/
|
||||
public function stop():Void
|
||||
{
|
||||
SoundMixer.__unregisterSoundChannel(this);
|
||||
|
||||
if (!__isValid) return;
|
||||
|
||||
#if lime
|
||||
__source.stop();
|
||||
#end
|
||||
__dispose();
|
||||
}
|
||||
|
||||
@:noCompletion private function __dispose():Void
|
||||
{
|
||||
if (!__isValid) return;
|
||||
|
||||
#if lime
|
||||
__source.onComplete.remove(source_onComplete);
|
||||
__source.dispose();
|
||||
__source = null;
|
||||
#end
|
||||
__isValid = false;
|
||||
}
|
||||
|
||||
@:noCompletion private function __updateTransform():Void
|
||||
{
|
||||
this.soundTransform = soundTransform;
|
||||
}
|
||||
|
||||
@:noCompletion private function __updatePeaks(time:Float):Bool
|
||||
{
|
||||
if (Math.abs(time - __lastPeakTime) < 8) return false;
|
||||
__lastPeakTime = time;
|
||||
|
||||
#if !macro
|
||||
if (!__isValid) return false;
|
||||
|
||||
var buffer = __source.buffer;
|
||||
var wordSize = buffer.bitsPerSample >> 3, byteSize = 1 << (buffer.bitsPerSample - 1);
|
||||
var pos = Math.floor(time * buffer.sampleRate / 1000 * buffer.channels * wordSize);
|
||||
var leftMin = 0, leftMax = 0, rightMin = 0, rightMax = 0, size = 0, buf;
|
||||
|
||||
#if lime_cffi
|
||||
var backend = __source.__backend, i = 0;
|
||||
if (backend.streamed) {
|
||||
size = backend.bufferLengths[i = backend.bufferLengths.length - backend.requestBuffers];
|
||||
buf = backend.bufferDatas[i].buffer;
|
||||
pos -= Math.floor(backend.bufferTimes[i] * buffer.sampleRate * buffer.channels * wordSize);
|
||||
while (pos > size) {
|
||||
if (++i >= backend.bufferLengths.length) return false;
|
||||
pos -= size;
|
||||
buf = backend.bufferDatas[i].buffer;
|
||||
size = backend.bufferLengths[i];
|
||||
}
|
||||
}
|
||||
else
|
||||
#end {
|
||||
buf = buffer.data #if !js .buffer #end;
|
||||
size = #if js buf.byteLength #else buf.length #end;
|
||||
}
|
||||
|
||||
var s = Math.floor(Math.min(buffer.sampleRate / 80, 512)), c = 0, b;
|
||||
pos -= pos % (buffer.channels * wordSize);
|
||||
|
||||
while (s > 0) {
|
||||
b = funkin.backend.utils.AudioAnalyzer.getByte(buf, pos, wordSize);
|
||||
if (c % 2 == 0) ((b > leftMax) ? (leftMax = b) : (if ((b = -b) > leftMin) (leftMin = b)));
|
||||
else ((b > rightMax) ? (rightMax = b) : (if ((b = -b) > rightMin) (rightMin = b)));
|
||||
if ((pos += wordSize) >= size) #if lime_cffi {
|
||||
if (!backend.streamed || ++i >= backend.bufferLengths.length) break;
|
||||
pos = 0;
|
||||
buf = backend.bufferDatas[i].buffer;
|
||||
size = backend.bufferLengths[i];
|
||||
}
|
||||
#else break; #end
|
||||
|
||||
if (++c > buffer.channels) {
|
||||
c = 0;
|
||||
s--;
|
||||
}
|
||||
}
|
||||
|
||||
if (buffer.channels == 1) __rightPeak = (__leftPeak = (leftMax + leftMin) / byteSize);
|
||||
else {
|
||||
__leftPeak = (leftMax + leftMin) / byteSize;
|
||||
__rightPeak = (rightMax + rightMin) / byteSize;
|
||||
}
|
||||
#end
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@:noCompletion private function __initAudioSource(source:#if lime AudioSource #else Dynamic #end):Void
|
||||
{
|
||||
#if lime
|
||||
__source = source;
|
||||
if (__source == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
__source.onComplete.add(source_onComplete);
|
||||
__isValid = true;
|
||||
|
||||
__source.play();
|
||||
#end
|
||||
}
|
||||
|
||||
// Get & Set Methods
|
||||
@:noCompletion private function get_position():Float
|
||||
{
|
||||
if (!__isValid) return 0;
|
||||
|
||||
#if lime
|
||||
return __source.currentTime + __source.offset;
|
||||
#else
|
||||
return 0;
|
||||
#end
|
||||
}
|
||||
|
||||
@:noCompletion private function set_position(value:Float):Float
|
||||
{
|
||||
if (!__isValid) return 0;
|
||||
|
||||
#if lime
|
||||
__source.currentTime = value - __source.offset;
|
||||
#end
|
||||
return value;
|
||||
}
|
||||
|
||||
@:noCompletion private function get_soundTransform():SoundTransform
|
||||
{
|
||||
return __soundTransform.clone();
|
||||
}
|
||||
|
||||
@:noCompletion private function set_soundTransform(value:SoundTransform):SoundTransform
|
||||
{
|
||||
if (value != null)
|
||||
{
|
||||
__soundTransform.pan = value.pan;
|
||||
__soundTransform.volume = value.volume;
|
||||
|
||||
var pan = SoundMixer.__soundTransform.pan + __soundTransform.pan;
|
||||
|
||||
if (pan < -1) pan = -1;
|
||||
if (pan > 1) pan = 1;
|
||||
|
||||
var volume = SoundMixer.__soundTransform.volume * __soundTransform.volume;
|
||||
|
||||
if (__isValid)
|
||||
{
|
||||
#if lime
|
||||
// TODO: implement SoundTransform.leftToRight, etc. with Native setAngles?
|
||||
__source.gain = volume;
|
||||
__source.pan = pan;
|
||||
return value;
|
||||
#end
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
@:noCompletion private function get_pitch():Float
|
||||
{
|
||||
if (!__isValid) return 1;
|
||||
|
||||
#if lime
|
||||
return __source.pitch;
|
||||
#else
|
||||
return 0;
|
||||
#end
|
||||
}
|
||||
|
||||
@:noCompletion private function set_pitch(value:Float):Float
|
||||
{
|
||||
if (!__isValid) return 1;
|
||||
|
||||
#if lime
|
||||
return __source.pitch = value;
|
||||
#else
|
||||
return 0;
|
||||
#end
|
||||
}
|
||||
|
||||
@:noCompletion private function get_loopTime():Float
|
||||
{
|
||||
if (!__isValid) return -1;
|
||||
|
||||
#if lime
|
||||
return __source.loopTime;
|
||||
#else
|
||||
return -1;
|
||||
#end
|
||||
}
|
||||
|
||||
@:noCompletion private function set_loopTime(value:Float):Float
|
||||
{
|
||||
if (!__isValid) return -1;
|
||||
|
||||
#if lime
|
||||
return __source.loopTime = value;
|
||||
#else
|
||||
return -1;
|
||||
#end
|
||||
}
|
||||
|
||||
@:noCompletion private function get_endTime():Null<Float>
|
||||
{
|
||||
if (!__isValid) return null;
|
||||
|
||||
#if lime
|
||||
return __source.length;
|
||||
#else
|
||||
return null;
|
||||
#end
|
||||
}
|
||||
|
||||
@:noCompletion private function set_endTime(value:Null<Float>):Null<Float>
|
||||
{
|
||||
if (!__isValid) return null;
|
||||
|
||||
#if lime
|
||||
return __source.length = value;
|
||||
#else
|
||||
return null;
|
||||
#end
|
||||
}
|
||||
|
||||
@:noCompletion private function get_loops():Int
|
||||
{
|
||||
if (!__isValid) return 0;
|
||||
|
||||
#if lime
|
||||
return __source.loops;
|
||||
#else
|
||||
return 0;
|
||||
#end
|
||||
}
|
||||
|
||||
@:noCompletion private function set_loops(value:Int):Int
|
||||
{
|
||||
if (!__isValid) return 0;
|
||||
|
||||
#if lime
|
||||
return __source.loops = value;
|
||||
#else
|
||||
return 0;
|
||||
#end
|
||||
}
|
||||
|
||||
@:noCompletion private function get_leftPeak():Float
|
||||
{
|
||||
__updatePeaks(get_position());
|
||||
return __leftPeak * (soundTransform == null ? 1 : soundTransform.volume);
|
||||
}
|
||||
|
||||
@:noCompletion private function get_rightPeak():Float
|
||||
{
|
||||
__updatePeaks(get_position());
|
||||
return __rightPeak * (soundTransform == null ? 1 : soundTransform.volume);
|
||||
}
|
||||
|
||||
|
||||
// Event Handlers
|
||||
@:noCompletion private function source_onComplete():Void
|
||||
{
|
||||
SoundMixer.__unregisterSoundChannel(this);
|
||||
|
||||
__dispose();
|
||||
dispatchEvent(new Event(Event.SOUND_COMPLETE));
|
||||
}
|
||||
|
||||
@:noCompletion private function get___audioSource():AudioSource return __source;
|
||||
@:noCompletion private function set___audioSource(source:AudioSource):AudioSource return __source = source;
|
||||
}
|
||||
#else
|
||||
typedef SoundChannel = flash.media.SoundChannel;
|
||||
#end
|
||||
@@ -1,859 +0,0 @@
|
||||
package openfl.utils;
|
||||
|
||||
#if !macro
|
||||
import funkin.backend.system.Main;
|
||||
import funkin.options.Options;
|
||||
import funkin.backend.system.OptimizedBitmapData;
|
||||
#end
|
||||
import openfl.utils._internal.Log;
|
||||
import openfl.display.BitmapData;
|
||||
import openfl.display.MovieClip;
|
||||
import openfl.display.Sprite;
|
||||
import openfl.events.Event;
|
||||
import openfl.events.EventDispatcher;
|
||||
import openfl.media.Sound;
|
||||
import openfl.text.Font;
|
||||
#if lime
|
||||
import lime.app.Promise;
|
||||
import lime.utils.AssetLibrary as LimeAssetLibrary;
|
||||
import lime.utils.Assets as LimeAssets;
|
||||
#end
|
||||
#if lime_vorbis
|
||||
import lime.media.AudioBuffer;
|
||||
import lime.media.vorbis.VorbisFile;
|
||||
#end
|
||||
|
||||
/**
|
||||
The Assets class provides a cross-platform interface to access
|
||||
embedded images, fonts, sounds and other resource files.
|
||||
|
||||
The contents are populated automatically when an application
|
||||
is compiled using the OpenFL command-line tools, based on the
|
||||
contents of the *.xml project file.
|
||||
|
||||
For most platforms, the assets are included in the same directory
|
||||
or package as the application, and the paths are handled
|
||||
automatically. For web content, the assets are preloaded before
|
||||
the start of the rest of the application. You can customize the
|
||||
preloader by extending the `NMEPreloader` class,
|
||||
and specifying a custom preloader using <window preloader="" />
|
||||
in the project file.
|
||||
**/
|
||||
#if !openfl_debug
|
||||
@:fileXml('tags="haxe,release"')
|
||||
@:noDebug
|
||||
#end
|
||||
@:access(openfl.display.BitmapData)
|
||||
@:access(openfl.display.Sprite)
|
||||
@:access(openfl.text.Font)
|
||||
@:access(openfl.utils.AssetLibrary)
|
||||
class Assets
|
||||
{
|
||||
public static var cache:IAssetCache = new AssetCache();
|
||||
|
||||
@:noCompletion private static var dispatcher:EventDispatcher #if !macro = new EventDispatcher() #end;
|
||||
private static var libraryBindings:Map<String, AssetLibrary> = new Map();
|
||||
|
||||
public static function addEventListener(type:String, listener:Dynamic, useCapture:Bool = false, priority:Int = 0, useWeakReference:Bool = false):Void
|
||||
{
|
||||
#if lime
|
||||
if (!LimeAssets.onChange.has(LimeAssets_onChange))
|
||||
{
|
||||
LimeAssets.onChange.add(LimeAssets_onChange);
|
||||
}
|
||||
#end
|
||||
|
||||
dispatcher.addEventListener(type, listener, useCapture, priority, useWeakReference);
|
||||
}
|
||||
|
||||
public static function dispatchEvent(event:Event):Bool
|
||||
{
|
||||
return dispatcher.dispatchEvent(event);
|
||||
}
|
||||
|
||||
/**
|
||||
Returns whether a specific asset exists
|
||||
@param id The ID or asset path for the asset
|
||||
@param type The asset type to match, or null to match any type
|
||||
@return Whether the requested asset ID and type exists
|
||||
**/
|
||||
public static function exists(id:String, type:AssetType = null):Bool
|
||||
{
|
||||
#if lime
|
||||
return LimeAssets.exists(id, cast type);
|
||||
#else
|
||||
return false;
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
Gets an instance of an embedded bitmap
|
||||
@usage var bitmap = new Bitmap (Assets.getBitmapData ("image.png"));
|
||||
@param id The ID or asset path for the bitmap
|
||||
@param useCache (Optional) Whether to allow use of the asset cache (Default: true)
|
||||
@param pushToGPU Whenever the image should be immediately pushed to GPU.
|
||||
@return A new BitmapData object
|
||||
**/
|
||||
public static function getBitmapData(id:String, useCache:Bool = true, pushToGPU:Bool = true):BitmapData
|
||||
{
|
||||
#if (lime && tools && !display)
|
||||
if (useCache && cache.enabled && cache.hasBitmapData(id))
|
||||
{
|
||||
var bitmapData = cache.getBitmapData(id);
|
||||
|
||||
if (isValidBitmapData(bitmapData) && (pushToGPU || bitmapData.readable))
|
||||
{
|
||||
return bitmapData;
|
||||
}
|
||||
}
|
||||
|
||||
var image = LimeAssets.getImage(id, false);
|
||||
|
||||
if (image != null)
|
||||
{
|
||||
#if flash
|
||||
var bitmapData = image.src;
|
||||
#else
|
||||
var bitmapData:BitmapData = null;
|
||||
#if !macro if (pushToGPU && !Main.forceGPUOnlyBitmapsOff && Options.gpuOnlyBitmaps) {
|
||||
bitmapData = new OptimizedBitmapData(0, 0, true, 0);
|
||||
bitmapData.__fromImage(image);
|
||||
} else #end {
|
||||
bitmapData = BitmapData.fromImage(image);
|
||||
}
|
||||
#end
|
||||
|
||||
if (useCache && cache.enabled)
|
||||
{
|
||||
cache.setBitmapData(id, bitmapData);
|
||||
}
|
||||
|
||||
return bitmapData;
|
||||
}
|
||||
#end
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
Gets an instance of an embedded binary asset
|
||||
@usage var bytes = Assets.getBytes ("file.zip");
|
||||
@param id The ID or asset path for the asset
|
||||
@return A new ByteArray object
|
||||
**/
|
||||
public static function getBytes(id:String):ByteArray
|
||||
{
|
||||
#if lime
|
||||
return LimeAssets.getBytes(id);
|
||||
#else
|
||||
return null;
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
Gets an instance of an embedded font
|
||||
@usage var fontName = Assets.getFont ("font.ttf").fontName;
|
||||
@param id The ID or asset path for the font
|
||||
@param useCache (Optional) Whether to allow use of the asset cache (Default: true)
|
||||
@return A new Font object
|
||||
**/
|
||||
public static function getFont(id:String, useCache:Bool = true):Font
|
||||
{
|
||||
#if (lime && tools && !display && !macro)
|
||||
if (useCache && cache.enabled && cache.hasFont(id))
|
||||
{
|
||||
return cache.getFont(id);
|
||||
}
|
||||
|
||||
var limeFont = LimeAssets.getFont(id, false);
|
||||
|
||||
if (limeFont != null)
|
||||
{
|
||||
#if flash
|
||||
var font = limeFont.src;
|
||||
#else
|
||||
var font = new Font();
|
||||
font.__fromLimeFont(limeFont);
|
||||
#end
|
||||
|
||||
if (useCache && cache.enabled)
|
||||
{
|
||||
cache.setFont(id, font);
|
||||
}
|
||||
|
||||
return font;
|
||||
}
|
||||
#end
|
||||
|
||||
return new Font();
|
||||
}
|
||||
|
||||
public static function getLibrary(name:String):#if lime LimeAssetLibrary #else AssetLibrary #end
|
||||
{
|
||||
#if lime
|
||||
return LimeAssets.getLibrary(name);
|
||||
#else
|
||||
return null;
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
Gets an instance of an included MovieClip
|
||||
@usage var movieClip = Assets.getMovieClip ("library:BouncingBall");
|
||||
@param id The ID for the MovieClip
|
||||
@return A new MovieClip object
|
||||
**/
|
||||
public static function getMovieClip(id:String):MovieClip
|
||||
{
|
||||
#if (lime && tools && !display)
|
||||
var libraryName = id.substring(0, id.indexOf(":"));
|
||||
var symbolName = id.substr(id.indexOf(":") + 1);
|
||||
var limeLibrary = getLibrary(libraryName);
|
||||
|
||||
if (limeLibrary != null)
|
||||
{
|
||||
if ((limeLibrary is AssetLibrary))
|
||||
{
|
||||
var library:AssetLibrary = cast limeLibrary;
|
||||
|
||||
if (library.exists(symbolName, cast AssetType.MOVIE_CLIP))
|
||||
{
|
||||
if (library.isLocal(symbolName, cast AssetType.MOVIE_CLIP))
|
||||
{
|
||||
return library.getMovieClip(symbolName);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.error("MovieClip asset \"" + id + "\" exists, but only asynchronously");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Log.error("There is no MovieClip asset with an ID of \"" + id + "\"");
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.error("There is no asset library named \"" + libraryName + "\"");
|
||||
}
|
||||
#end
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function getMusic(id:String, useCache:Bool = true, staticFallback:Bool = true):Sound
|
||||
{
|
||||
if (useCache && staticFallback && cache.enabled && cache.hasSound(id)) {
|
||||
var sound = cache.getSound(id);
|
||||
if (isValidSound(sound)) return sound;
|
||||
}
|
||||
/*#if (lime_vorbis && lime > "7.9.0" && !macro)
|
||||
if (Options.streamedMusic) {
|
||||
var bytes = getBytes(id);
|
||||
if (bytes == null) return null;
|
||||
// TODO: What if it is a WAV or non-Vorbis file?
|
||||
var vorbisFile = VorbisFile.fromBytes(bytes);
|
||||
if (vorbisFile != null) return Sound.fromAudioBuffer(AudioBuffer.fromVorbisFile(vorbisFile));
|
||||
}
|
||||
#end*/
|
||||
return if (staticFallback) getSound(id, useCache); else null;
|
||||
}
|
||||
|
||||
/**
|
||||
Gets the file path (if available) for an asset
|
||||
@usage var path = Assets.getPath ("file.txt");
|
||||
@param id The ID or asset path for the asset
|
||||
@return The path to the asset, or null if it does not exist
|
||||
**/
|
||||
public static function getPath(id:String):String
|
||||
{
|
||||
#if lime
|
||||
return LimeAssets.getPath(id);
|
||||
#else
|
||||
return null;
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
Gets an instance of an embedded sound
|
||||
@usage var sound = Assets.getSound ("sound.wav");
|
||||
@param id The ID or asset path for the sound
|
||||
@param useCache (Optional) Whether to allow use of the asset cache (Default: true)
|
||||
@return A new Sound object
|
||||
**/
|
||||
public static function getSound(id:String, useCache:Bool = true):Sound
|
||||
{
|
||||
#if (lime && tools && !display)
|
||||
if (useCache && cache.enabled && cache.hasSound(id))
|
||||
{
|
||||
var sound = cache.getSound(id);
|
||||
|
||||
if (isValidSound(sound))
|
||||
{
|
||||
return sound;
|
||||
}
|
||||
}
|
||||
|
||||
var buffer = LimeAssets.getAudioBuffer(id, false);
|
||||
|
||||
if (buffer != null)
|
||||
{
|
||||
#if flash
|
||||
var sound = buffer.src;
|
||||
#else
|
||||
var sound = Sound.fromAudioBuffer(buffer);
|
||||
#end
|
||||
|
||||
if (useCache && cache.enabled)
|
||||
{
|
||||
cache.setSound(id, sound);
|
||||
}
|
||||
|
||||
return sound;
|
||||
}
|
||||
#end
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
Gets an instance of an embedded text asset
|
||||
@usage var text = Assets.getText ("text.txt");
|
||||
@param id The ID or asset path for the asset
|
||||
@return A new String object
|
||||
**/
|
||||
public static function getText(id:String):String
|
||||
{
|
||||
#if lime
|
||||
return LimeAssets.getText(id);
|
||||
#else
|
||||
return null;
|
||||
#end
|
||||
}
|
||||
|
||||
public static function hasEventListener(type:String):Bool
|
||||
{
|
||||
return dispatcher.hasEventListener(type);
|
||||
}
|
||||
|
||||
public static function hasLibrary(name:String):Bool
|
||||
{
|
||||
#if lime
|
||||
return LimeAssets.hasLibrary(name);
|
||||
#else
|
||||
return false;
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
Connects a user-defined class to a related asset class.
|
||||
|
||||
This method call is added to the beginning of user-defined class constructors when
|
||||
the `@:bind` meta-data is used. This allows insertion of related asset resources in
|
||||
compatible super classes, such as `openfl.display.MovieClip`.
|
||||
@param className The registered class name of the asset constructor
|
||||
@param instance The current class instance to be bound (default is null)
|
||||
@return Whether asset binding was successful
|
||||
**/
|
||||
public static function initBinding(className:String, instance:Dynamic = null):Void
|
||||
{
|
||||
if (libraryBindings.exists(className))
|
||||
{
|
||||
var library = libraryBindings.get(className);
|
||||
#if !flash
|
||||
if (instance == null)
|
||||
{
|
||||
Sprite.__constructor = function(instance:Sprite)
|
||||
{
|
||||
instance.__bind(library, className);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Sprite.__constructor = null;
|
||||
instance.__bind(library, className);
|
||||
}
|
||||
#else
|
||||
// TODO: Consolidate behavior
|
||||
library.bind(className);
|
||||
#end
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.warn("No asset is registered as \"" + className + "\"");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Returns whether an asset is "local", and therefore can be loaded synchronously
|
||||
@param id The ID or asset path for the asset
|
||||
@param type The asset type to match, or null to match any type
|
||||
@param useCache (Optional) Whether to allow use of the asset cache (Default: true)
|
||||
@return Whether the asset is local
|
||||
**/
|
||||
public static function isLocal(id:String, type:AssetType = null, useCache:Bool = true):Bool
|
||||
{
|
||||
#if (lime && tools && !display)
|
||||
if (useCache && cache.enabled)
|
||||
{
|
||||
if (type == AssetType.IMAGE || type == null)
|
||||
{
|
||||
if (cache.hasBitmapData(id)) return true;
|
||||
}
|
||||
|
||||
if (type == AssetType.FONT || type == null)
|
||||
{
|
||||
if (cache.hasFont(id)) return true;
|
||||
}
|
||||
|
||||
if (type == AssetType.SOUND || type == AssetType.MUSIC || type == null)
|
||||
{
|
||||
if (cache.hasSound(id)) return true;
|
||||
}
|
||||
}
|
||||
|
||||
var libraryName = id.substring(0, id.indexOf(":"));
|
||||
var symbolName = id.substr(id.indexOf(":") + 1);
|
||||
var library = getLibrary(libraryName);
|
||||
|
||||
if (library != null)
|
||||
{
|
||||
return library.isLocal(symbolName, cast type);
|
||||
}
|
||||
#end
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@:analyzer(ignore) private static function isValidBitmapData(bitmapData:BitmapData):Bool
|
||||
{
|
||||
#if (lime && tools && !display)
|
||||
#if flash
|
||||
try
|
||||
{
|
||||
bitmapData.width;
|
||||
return true;
|
||||
}
|
||||
catch (e:Dynamic)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
return (bitmapData != null && #if !lime_hybrid bitmapData.image != null #else bitmapData.__handle != null #end);
|
||||
#end
|
||||
#else
|
||||
return true;
|
||||
#end
|
||||
}
|
||||
|
||||
@:noCompletion private static function isValidSound(sound:Sound):Bool
|
||||
{
|
||||
#if ((tools && !display) && (cpp || neko || nodejs))
|
||||
return true;
|
||||
// return (sound.__handle != null && sound.__handle != 0);
|
||||
#else
|
||||
return true;
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
Returns a list of all embedded assets (by type)
|
||||
@param type The asset type to match, or null to match any type
|
||||
@return An array of asset ID values
|
||||
**/
|
||||
public static function list(type:AssetType = null):Array<String>
|
||||
{
|
||||
#if lime
|
||||
return LimeAssets.list(cast type);
|
||||
#else
|
||||
return [];
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
Loads an included bitmap asset asynchronously
|
||||
@usage Assets.loadBitmapData ("image.png").onComplete (handleImage);
|
||||
@param id The ID or asset path for the asset
|
||||
@param useCache (Optional) Whether to allow use of the asset cache (Default: true)
|
||||
@return Returns a Future<BitmapData>
|
||||
**/
|
||||
public static function loadBitmapData(id:String, useCache:Null<Bool> = true):Future<BitmapData>
|
||||
{
|
||||
if (useCache == null) useCache = true;
|
||||
|
||||
#if (lime && tools && !display)
|
||||
var promise = new Promise<BitmapData>();
|
||||
|
||||
if (useCache && cache.enabled && cache.hasBitmapData(id))
|
||||
{
|
||||
var bitmapData = cache.getBitmapData(id);
|
||||
|
||||
if (isValidBitmapData(bitmapData))
|
||||
{
|
||||
promise.complete(bitmapData);
|
||||
return promise.future;
|
||||
}
|
||||
}
|
||||
|
||||
LimeAssets.loadImage(id, false).onComplete(function(image)
|
||||
{
|
||||
if (image != null)
|
||||
{
|
||||
#if flash
|
||||
var bitmapData = image.src;
|
||||
#else
|
||||
var bitmapData:BitmapData = null;
|
||||
#if !macro if (!Main.forceGPUOnlyBitmapsOff && Options.gpuOnlyBitmaps) {
|
||||
bitmapData = new OptimizedBitmapData(0, 0, true, 0);
|
||||
bitmapData.__fromImage(image);
|
||||
} else #end {
|
||||
bitmapData = BitmapData.fromImage(image);
|
||||
}
|
||||
#end
|
||||
|
||||
if (useCache && cache.enabled)
|
||||
{
|
||||
cache.setBitmapData(id, bitmapData);
|
||||
}
|
||||
|
||||
promise.complete(bitmapData);
|
||||
}
|
||||
else
|
||||
{
|
||||
promise.error("[Assets] Could not load Image \"" + id + "\"");
|
||||
}
|
||||
}).onError(promise.error).onProgress(promise.progress);
|
||||
|
||||
return promise.future;
|
||||
#else
|
||||
return Future.withValue(getBitmapData(id, useCache));
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
Loads an included byte asset asynchronously
|
||||
@usage Assets.loadBytes ("file.zip").onComplete (handleBytes);
|
||||
@param id The ID or asset path for the asset
|
||||
@return Returns a Future<ByteArray>
|
||||
**/
|
||||
public static function loadBytes(id:String):Future<ByteArray>
|
||||
{
|
||||
#if lime
|
||||
var promise = new Promise<ByteArray>();
|
||||
var future = LimeAssets.loadBytes(id);
|
||||
|
||||
future.onComplete(function(bytes) promise.complete(bytes));
|
||||
future.onProgress(function(progress, total) promise.progress(progress, total));
|
||||
future.onError(function(msg) promise.error(msg));
|
||||
|
||||
return promise.future;
|
||||
#else
|
||||
return Future.withValue(getBytes(id));
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
Loads an included font asset asynchronously
|
||||
@usage Assets.loadFont ("font.ttf").onComplete (handleFont);
|
||||
@param id The ID or asset path for the asset
|
||||
@param useCache (Optional) Whether to allow use of the asset cache (Default: true)
|
||||
@return Returns a Future<Font>
|
||||
**/
|
||||
public static function loadFont(id:String, useCache:Null<Bool> = true):Future<Font>
|
||||
{
|
||||
if (useCache == null) useCache = true;
|
||||
|
||||
#if (lime && tools && !display && !macro)
|
||||
var promise = new Promise<Font>();
|
||||
|
||||
if (useCache && cache.enabled && cache.hasFont(id))
|
||||
{
|
||||
promise.complete(cache.getFont(id));
|
||||
return promise.future;
|
||||
}
|
||||
|
||||
LimeAssets.loadFont(id)
|
||||
.onComplete(function(limeFont)
|
||||
{
|
||||
#if flash
|
||||
var font = limeFont.src;
|
||||
#else
|
||||
var font = new Font();
|
||||
font.__fromLimeFont(limeFont);
|
||||
#end
|
||||
|
||||
if (useCache && cache.enabled)
|
||||
{
|
||||
cache.setFont(id, font);
|
||||
}
|
||||
|
||||
promise.complete(font);
|
||||
})
|
||||
.onError(promise.error)
|
||||
.onProgress(promise.progress);
|
||||
|
||||
return promise.future;
|
||||
#else
|
||||
return Future.withValue(getFont(id, useCache));
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
Load an included AssetLibrary
|
||||
@param name The name of the AssetLibrary to load
|
||||
@return Returns a Future<AssetLibrary>
|
||||
**/
|
||||
public static function loadLibrary(name:String):#if java Future<LimeAssetLibrary> #else Future<AssetLibrary> #end
|
||||
{
|
||||
#if lime
|
||||
return LimeAssets.loadLibrary(name).then(function(library)
|
||||
{
|
||||
var _library:AssetLibrary = null;
|
||||
|
||||
if (library != null)
|
||||
{
|
||||
if ((library is AssetLibrary))
|
||||
{
|
||||
_library = cast library;
|
||||
}
|
||||
else
|
||||
{
|
||||
_library = new AssetLibrary();
|
||||
_library.__proxy = library;
|
||||
LimeAssets.registerLibrary(name, _library);
|
||||
}
|
||||
}
|
||||
|
||||
return Future.withValue(_library);
|
||||
});
|
||||
#else
|
||||
return cast Future.withError("Cannot load library");
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
Loads an included music asset asynchronously
|
||||
@usage Assets.loadMusic ("music.ogg").onComplete (handleMusic);
|
||||
@param id The ID or asset path for the asset
|
||||
@param useCache (Optional) Whether to allow use of the asset cache (Default: true)
|
||||
@return Returns a Future<Sound>
|
||||
**/
|
||||
public static function loadMusic(id:String, useCache:Null<Bool> = true):Future<Sound>
|
||||
{
|
||||
if (useCache == null) useCache = true;
|
||||
|
||||
#if lime
|
||||
#if !html5
|
||||
var promise = new Promise<Sound>();
|
||||
|
||||
LimeAssets.loadAudioBuffer(id, useCache)
|
||||
.onComplete(function(buffer)
|
||||
{
|
||||
if (buffer != null)
|
||||
{
|
||||
#if flash
|
||||
var sound = buffer.src;
|
||||
#else
|
||||
var sound = Sound.fromAudioBuffer(buffer);
|
||||
#end
|
||||
|
||||
if (useCache && cache.enabled)
|
||||
{
|
||||
cache.setSound(id, sound);
|
||||
}
|
||||
|
||||
promise.complete(sound);
|
||||
}
|
||||
else
|
||||
{
|
||||
promise.error("[Assets] Could not load Sound \"" + id + "\"");
|
||||
}
|
||||
})
|
||||
.onError(promise.error)
|
||||
.onProgress(promise.progress);
|
||||
return promise.future;
|
||||
#else
|
||||
var future = new Future<Sound>(function() return getMusic(id, useCache));
|
||||
return future;
|
||||
#end
|
||||
#else
|
||||
return Future.withValue(getMusic(id, useCache));
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
Loads an included MovieClip asset asynchronously
|
||||
@usage Assets.loadMovieClip ("library:BouncingBall").onComplete (handleMovieClip);
|
||||
@param id The ID for the asset
|
||||
@param useCache (Optional) Whether to allow use of the asset cache (Default: true)
|
||||
@return Returns a Future<MovieClip>
|
||||
**/
|
||||
public static function loadMovieClip(id:String):Future<MovieClip>
|
||||
{
|
||||
#if (lime && tools && !display)
|
||||
var promise = new Promise<MovieClip>();
|
||||
|
||||
var libraryName = id.substring(0, id.indexOf(":"));
|
||||
var symbolName = id.substr(id.indexOf(":") + 1);
|
||||
var limeLibrary = getLibrary(libraryName);
|
||||
|
||||
if (limeLibrary != null)
|
||||
{
|
||||
if ((limeLibrary is AssetLibrary))
|
||||
{
|
||||
var library:AssetLibrary = cast limeLibrary;
|
||||
|
||||
if (library.exists(symbolName, cast AssetType.MOVIE_CLIP))
|
||||
{
|
||||
promise.completeWith(library.loadMovieClip(symbolName));
|
||||
return promise.future;
|
||||
}
|
||||
}
|
||||
|
||||
promise.error("[Assets] There is no MovieClip asset with an ID of \"" + id + "\"");
|
||||
}
|
||||
else
|
||||
{
|
||||
promise.error("[Assets] There is no asset library named \"" + libraryName + "\"");
|
||||
}
|
||||
|
||||
return promise.future;
|
||||
#else
|
||||
return Future.withValue(getMovieClip(id));
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
Loads an included sound asset asynchronously
|
||||
@usage Assets.loadSound ("sound.wav").onComplete (handleSound);
|
||||
@param id The ID or asset path for the asset
|
||||
@param useCache (Optional) Whether to allow use of the asset cache (Default: true)
|
||||
@return Returns a Future<Sound>
|
||||
**/
|
||||
public static function loadSound(id:String, useCache:Null<Bool> = true):Future<Sound>
|
||||
{
|
||||
if (useCache == null) useCache = true;
|
||||
|
||||
#if lime
|
||||
var promise = new Promise<Sound>();
|
||||
|
||||
LimeAssets.loadAudioBuffer(id, useCache)
|
||||
.onComplete(function(buffer)
|
||||
{
|
||||
if (buffer != null)
|
||||
{
|
||||
#if flash
|
||||
var sound = buffer.src;
|
||||
#else
|
||||
var sound = Sound.fromAudioBuffer(buffer);
|
||||
#end
|
||||
|
||||
if (useCache && cache.enabled)
|
||||
{
|
||||
cache.setSound(id, sound);
|
||||
}
|
||||
|
||||
promise.complete(sound);
|
||||
}
|
||||
else
|
||||
{
|
||||
promise.error("[Assets] Could not load Sound \"" + id + "\"");
|
||||
}
|
||||
})
|
||||
.onError(promise.error)
|
||||
.onProgress(promise.progress);
|
||||
return promise.future;
|
||||
#else
|
||||
return Future.withValue(getSound(id, useCache));
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
Loads an included text asset asynchronously
|
||||
@usage Assets.loadText ("text.txt").onComplete (handleString);
|
||||
@param id The ID or asset path for the asset
|
||||
@param useCache (Optional) Whether to allow use of the asset cache (Default: true)
|
||||
@return Returns a Future<String>
|
||||
**/
|
||||
public static function loadText(id:String):Future<String>
|
||||
{
|
||||
#if lime
|
||||
var future = LimeAssets.loadText(id);
|
||||
return future;
|
||||
#else
|
||||
return Future.withValue(getText(id));
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
Registers an AssetLibrary binding for use with @:bind or Assets.bind
|
||||
@param className The class name to use for the binding
|
||||
@param method The AssetLibrary responsible for the binding
|
||||
**/
|
||||
public static function registerBinding(className:String, library:AssetLibrary):Void
|
||||
{
|
||||
libraryBindings.set(className, library);
|
||||
}
|
||||
|
||||
/**
|
||||
Registers a new AssetLibrary with the Assets class
|
||||
@param name The name (prefix) to use for the library
|
||||
@param library An AssetLibrary instance to register
|
||||
**/
|
||||
public static function registerLibrary(name:String, library:AssetLibrary):Void
|
||||
{
|
||||
#if lime
|
||||
LimeAssets.registerLibrary(name, library);
|
||||
#end
|
||||
}
|
||||
|
||||
public static function removeEventListener(type:String, listener:Dynamic, capture:Bool = false):Void
|
||||
{
|
||||
dispatcher.removeEventListener(type, listener, capture);
|
||||
}
|
||||
|
||||
@:noCompletion private static function resolveClass(name:String):Class<Dynamic>
|
||||
{
|
||||
return Type.resolveClass(name);
|
||||
}
|
||||
|
||||
@:noCompletion private static function resolveEnum(name:String):Enum<Dynamic>
|
||||
{
|
||||
var value = Type.resolveEnum(name);
|
||||
|
||||
#if flash
|
||||
if (value == null)
|
||||
{
|
||||
return cast Type.resolveClass(name);
|
||||
}
|
||||
#end
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public static function unloadLibrary(name:String):Void
|
||||
{
|
||||
#if lime
|
||||
LimeAssets.unloadLibrary(name);
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
Unregisters an AssetLibrary binding for use with @:bind or Assets.bind
|
||||
@param className The class name to use for the binding
|
||||
@param method The AssetLibrary responsible for the binding
|
||||
**/
|
||||
public static function unregisterBinding(className:String, library:AssetLibrary):Void
|
||||
{
|
||||
if (libraryBindings.exists(className) && libraryBindings.get(className) == library)
|
||||
{
|
||||
libraryBindings.remove(className);
|
||||
}
|
||||
}
|
||||
|
||||
// Event Handlers
|
||||
@:noCompletion private static function LimeAssets_onChange():Void
|
||||
{
|
||||
dispatchEvent(new Event(Event.CHANGE));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user