Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4950ee3b7c | ||
|
|
e07ba7fc46 | ||
|
|
27b9788a04 | ||
|
|
4765dfaa02 | ||
|
|
475a026604 | ||
|
|
8e463d9247 | ||
|
|
1bd447a56e | ||
|
|
9b1d30339f |
@@ -0,0 +1,26 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023-2026 Cory Petkovsek and Contributors
|
||||
Copyright (c) 2021 J. Cuéllar
|
||||
|
||||
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.
|
||||
|
||||
----
|
||||
|
||||
Third party assets have their own licenses found under assets/thirdparty.
|
||||
@@ -0,0 +1,73 @@
|
||||

|
||||
|
||||
|
||||
# Sky3D
|
||||
|
||||
A dynamic day/night cycle for Godot Engine 4, written in GDScript.
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
* Supports Godot 4.3+, Forward, Mobile, and Compatibility renderers
|
||||
* Automatically rotating sun, moon, and stars, with moon phases
|
||||
* Dynamic atmosphere, fog, and clouds that change with the day cycle
|
||||
* Consolidated controls to manage lighting and camera exposure
|
||||
* Management of game time: current time, day length, day or night
|
||||
|
||||
|
||||
## Screenshots
|
||||
|
||||

|
||||

|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
* Clone or download the repository.
|
||||
* Open the project in Godot and run `demo/Sky3DDemo.tscn` to test it.
|
||||
* Copy `addons/sky_3d` into your project `addons` directory. Create the folder if missing.
|
||||
* Open `Project -> Project Settings -> Plugins` and enable the plugin.
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
* Create or open a Scene.
|
||||
* Remove any existing `WorldEnvironment` node.
|
||||
* Create a new `Sky3D` node.
|
||||
* Customize the settings of the `Sky3D`, `Sky3D/Environment`, `TimeOfDay`, `SkyDome`, `SunLight`, and `MoonLight` nodes. Some settings like light energy, color, and angle are driven by Sky3D and not directly changeable on the light nodes. You'll know if they are reset on time updates. Adjust those settings in `Sky3D` or `SkyDome`.
|
||||
|
||||
|
||||
## Compatibility Renderer Note
|
||||
This render needs a bit of adjustment to show similar results to Vulkan:
|
||||
* `Sky3D / Sky Contribution = 0.75`
|
||||
* `SkyDome / Fog / Fog Density = 0.01`
|
||||
|
||||
|
||||
## Documentation
|
||||
|
||||
The documentation is built in to Godot. Look for tooltips in the inspector where available, or press F1 and search for `Sky3D`, `SkyDome`, `TimeOfDay`.
|
||||
|
||||
|
||||
## Support
|
||||
|
||||
For support, join our [Discord server](https://tokisan.com/discord).
|
||||
|
||||
|
||||
## Credit
|
||||
|
||||
Developed for the Godot community by:
|
||||
|||
|
||||
|--|--|
|
||||
| **Cory Petkovsek, Tokisan Games** | [<img src="https://github.com/dmhendricks/signature-social-icons/blob/master/icons/round-flat-filled/35px/twitter.png?raw=true" width="24"/>](https://twitter.com/TokisanGames) [<img src="https://github.com/dmhendricks/signature-social-icons/blob/master/icons/round-flat-filled/35px/github.png?raw=true" width="24"/>](https://github.com/TokisanGames) [<img src="https://github.com/dmhendricks/signature-social-icons/blob/master/icons/round-flat-filled/35px/www.png?raw=true" width="24"/>](https://tokisan.com/) [<img src="https://github.com/dmhendricks/signature-social-icons/blob/master/icons/round-flat-filled/35px/discord.png?raw=true" width="24"/>](https://tokisan.com/discord) [<img src="https://github.com/dmhendricks/signature-social-icons/blob/master/icons/round-flat-filled/35px/youtube.png?raw=true" width="24"/>](https://www.youtube.com/@TokisanGames)|
|
||||
|
||||
**And all of the wonderful contributors shown in the right sidebar of the [github repository](https://github.com/TokisanGames/Sky3D/).**
|
||||
|
||||
The original version of this plugin, *TimeOfDay v1*, was made by [J. Cuéllar](https://twitter.com/JayKuellar) for Godot 3. You can find it in the `godot3` branch. The original repository was deleted. We revived it, ported the GDScript version to Godot 4, and have continued to build on it to produce Sky3D.
|
||||
|
||||
|
||||
|
||||
## License
|
||||
|
||||
This addon has been released under the [MIT License](LICENSE.txt).
|
||||
|
||||
If using the star map assets, you must [credit the author](https://github.com/TokisanGames/Sky3D/blob/main/addons/sky_3d/assets/thirdparty/textures/milkyway/LICENSE.md).
|
||||
@@ -0,0 +1,8 @@
|
||||
## Attribution for Third Party Assets
|
||||
|
||||
`Milkyway.jpg` "[The Milky Way panorama](https://www.eso.org/public/images/eso0932a/)" by ESO/S. Brunier, licensed under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/
|
||||
).
|
||||
[Read more](/addons/sky_3d/assets/thirdparty/textures/milkyway/LICENSE.md)
|
||||
|
||||
`MoonMap.png` Copyright (c) 2019 GPoSM, MIT License
|
||||
[Read more](/addons/sky_3d/assets/thirdparty/textures/moon/LICENSE.md)
|
||||
@@ -0,0 +1,7 @@
|
||||
[gd_resource type="NoiseTexture2D" load_steps=2 format=3 uid="uid://cfqk60lpl5ljv"]
|
||||
|
||||
[sub_resource type="FastNoiseLite" id="1"]
|
||||
|
||||
[resource]
|
||||
seamless = true
|
||||
noise = SubResource("1")
|
||||
@@ -0,0 +1,5 @@
|
||||
[gd_resource type="Curve" format=3 uid="uid://70482fhm3qg7"]
|
||||
|
||||
[resource]
|
||||
_data = [Vector2(0, 0), 0.0, 0.0, 0, 0, Vector2(0.617886, 0), 0.0, 0.0467088, 1, 0, Vector2(0.699187, 1), 0.0, 0.0, 0, 0, Vector2(1, 1), 0.0, 0.0, 0, 0]
|
||||
point_count = 4
|
||||
|
After Width: | Height: | Size: 860 B |
@@ -0,0 +1,40 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://bwooawylkf7f2"
|
||||
path="res://.godot/imported/SkyIcon.png-7b3cbfa68354965cdbfdba8b95dacf67.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/sky_3d/assets/textures/SkyIcon.png"
|
||||
dest_files=["res://.godot/imported/SkyIcon.png-7b3cbfa68354965cdbfdba8b95dacf67.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
|
After Width: | Height: | Size: 48 KiB |
@@ -0,0 +1,41 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://djpfuyxkryegn"
|
||||
path.s3tc="res://.godot/imported/noise.jpg-b47d5d36d9abfdaec2a09c140a78ba24.s3tc.ctex"
|
||||
metadata={
|
||||
"imported_formats": ["s3tc_bptc"],
|
||||
"vram_texture": true
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/sky_3d/assets/textures/noise.jpg"
|
||||
dest_files=["res://.godot/imported/noise.jpg-b47d5d36d9abfdaec2a09c140a78ba24.s3tc.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=2
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=true
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=0
|
||||
|
After Width: | Height: | Size: 142 KiB |
@@ -0,0 +1,41 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://bd7u40ws8s57f"
|
||||
path.s3tc="res://.godot/imported/noise2.png-37a5cbd5451b4b9c2c18ab32099e6965.s3tc.ctex"
|
||||
metadata={
|
||||
"imported_formats": ["s3tc_bptc"],
|
||||
"vram_texture": true
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/sky_3d/assets/textures/noise2.png"
|
||||
dest_files=["res://.godot/imported/noise2.png-37a5cbd5451b4b9c2c18ab32099e6965.s3tc.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=2
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
|
After Width: | Height: | Size: 123 KiB |
@@ -0,0 +1,41 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://cecwdqjol0ckd"
|
||||
path.s3tc="res://.godot/imported/noiseClouds.png-07511094ef8b72a49cc860b35c60d0fd.s3tc.ctex"
|
||||
metadata={
|
||||
"imported_formats": ["s3tc_bptc"],
|
||||
"vram_texture": true
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/sky_3d/assets/textures/noiseClouds.png"
|
||||
dest_files=["res://.godot/imported/noiseClouds.png-07511094ef8b72a49cc860b35c60d0fd.s3tc.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=2
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=true
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=0
|
||||
@@ -0,0 +1,40 @@
|
||||
# Attribution
|
||||
`Milkyway.jpg` "[The Milky Way panorama](https://www.eso.org/public/images/eso0932a/)" by ESO/S. Brunier, licensed under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/
|
||||
).
|
||||
|
||||
`StarField.jpg` is the same image modified to hide all but the brightest stars.
|
||||
|
||||
Your game credits should include attribution similar to the above with a link to the license where reasonable.
|
||||
|
||||
e.g. `The Milky Way panorama by ESO/S. Brunier, licensed under CC BY 4.0 and used in original and modified forms.`
|
||||
|
||||
Higher resolutions and more information are available at: https://www.eso.org/public/images/eso0932a/
|
||||
|
||||
Licensing questions answered here: https://www.eso.org/public/copyright/
|
||||
|
||||
## License
|
||||
[Creative Commons Attribution 4.0 International License](https://creativecommons.org/licenses/by/4.0/)
|
||||
|
||||
|
||||
## Relevant FAQs
|
||||
|
||||
[From ESO](https://www.eso.org/public/copyright/)
|
||||
|
||||
```
|
||||
Q: I am making an electronic game. Where should I place the credit?
|
||||
A: As long as the credit is clearly visible to all users and reproduced in full, for instance in a splashscreen, all is fine.
|
||||
```
|
||||
|
||||
[From CC](https://wiki.creativecommons.org/wiki/Recommended_practices_for_attribution#This_is_a_great_attribution)
|
||||
|
||||
```
|
||||
This is a great attribution
|
||||
"Creative Commons 10th Birthday Celebration San Francisco"(source link) by Timothy Vollmer is licensed under CC BY 4.0 (license link)
|
||||
|
||||
Let’s go through TASL:
|
||||
Title? "Creative Commons 10th Birthday Celebration San Francisco"
|
||||
Author? "Timothy Vollmer" - linked to his profile page
|
||||
Source? "Creative Commons 10th Birthday Celebration San Francisco" - linked to original Flickr page
|
||||
License? "CC BY 4.0" - linked to license deed
|
||||
Most importantly, this attribution reasonably includes all the relevant information provided by the author.
|
||||
```
|
||||
|
After Width: | Height: | Size: 4.6 MiB |
@@ -0,0 +1,41 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://c1vwcdcdvb74a"
|
||||
path.s3tc="res://.godot/imported/Milkyway.jpg-a06044e407cf6953ab6e46a2f0f17f3a.s3tc.ctex"
|
||||
metadata={
|
||||
"imported_formats": ["s3tc_bptc"],
|
||||
"vram_texture": true
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/sky_3d/assets/thirdparty/textures/milkyway/Milkyway.jpg"
|
||||
dest_files=["res://.godot/imported/Milkyway.jpg-a06044e407cf6953ab6e46a2f0f17f3a.s3tc.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=2
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=true
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=0
|
||||
|
After Width: | Height: | Size: 115 KiB |
@@ -0,0 +1,41 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://bm7dot7t7u1q4"
|
||||
path.s3tc="res://.godot/imported/StarField.jpg-9d224e20fcbe8fdc7ff5028dae269839.s3tc.ctex"
|
||||
metadata={
|
||||
"imported_formats": ["s3tc_bptc"],
|
||||
"vram_texture": true
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/sky_3d/assets/thirdparty/textures/milkyway/StarField.jpg"
|
||||
dest_files=["res://.godot/imported/StarField.jpg-9d224e20fcbe8fdc7ff5028dae269839.s3tc.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=2
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=true
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=0
|
||||
@@ -0,0 +1,23 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 GPoSM
|
||||
|
||||
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.
|
||||
|
||||
Source: https://www.highend3d.com/downloads/3d-textures/c/16k-earth-w-4k-moon-free#dialog_license
|
||||
|
After Width: | Height: | Size: 5.8 MiB |
@@ -0,0 +1,41 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://2r8ylu6rg5dp"
|
||||
path.s3tc="res://.godot/imported/MoonMap.png-81d542c3fafff4e5b235a177af6f3221.s3tc.ctex"
|
||||
metadata={
|
||||
"imported_formats": ["s3tc_bptc"],
|
||||
"vram_texture": true
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/sky_3d/assets/thirdparty/textures/moon/MoonMap.png"
|
||||
dest_files=["res://.godot/imported/MoonMap.png-81d542c3fafff4e5b235a177af6f3221.s3tc.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=2
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=true
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=0
|
||||
@@ -0,0 +1,7 @@
|
||||
[plugin]
|
||||
|
||||
name="Sky3D"
|
||||
description="Atmospheric Day/Night Cycle"
|
||||
author="Cory Petkovsek, J. Cuéllar, and Contributors"
|
||||
version="2.1"
|
||||
script="src/Plugin.gd"
|
||||
@@ -0,0 +1,161 @@
|
||||
// Copyright (c) 2023-2025 Cory Petkovsek and Contributors
|
||||
// Copyright (c) 2021 J. Cuellar
|
||||
|
||||
shader_type spatial;
|
||||
render_mode blend_mix, cull_front, unshaded, fog_disabled;
|
||||
|
||||
|
||||
uniform vec2 color_correction;
|
||||
|
||||
uniform float sea_level: hint_range(-2048,2048,.01) = 0.0;
|
||||
uniform float fog_density = .0001;
|
||||
uniform float fog_rayleigh_depth = .115;
|
||||
uniform float fog_mie_depth = 0.;
|
||||
uniform float fog_falloff = 3.;
|
||||
uniform float fog_start = 0.;
|
||||
uniform float fog_end = 1000.;
|
||||
uniform vec3 sun_direction = vec3(.25, -.25, .25);
|
||||
uniform vec3 moon_direction = vec3(.45, -.25, .45);
|
||||
|
||||
uniform float atm_darkness = .5;
|
||||
uniform float atm_sun_intensity = 30.;
|
||||
uniform vec4 atm_day_tint: source_color = vec4(.78, .85, .98, 1.);
|
||||
uniform vec4 atm_horizon_light_tint: source_color = vec4(.98, .73, .49, 1.);
|
||||
uniform vec4 atm_night_tint: source_color = vec4(.16, .2, .25, 1.);
|
||||
uniform vec3 atm_level_params = vec3(1., 0., 0.);
|
||||
uniform float atm_thickness = .7;
|
||||
|
||||
uniform vec3 atm_beta_ray;
|
||||
uniform vec3 atm_beta_mie;
|
||||
|
||||
uniform vec3 atm_sun_partial_mie_phase;
|
||||
uniform vec4 atm_sun_mie_tint: source_color = vec4(1.);
|
||||
uniform float atm_sun_mie_intensity = 1.;
|
||||
|
||||
uniform vec3 atm_moon_partial_mie_phase;
|
||||
uniform vec4 atm_moon_mie_tint: source_color = vec4(.13, .18, .29, 1.);
|
||||
uniform float atm_moon_mie_intensity = .7;
|
||||
|
||||
uniform sampler2D DEPTH_TEXTURE : hint_depth_texture, filter_linear_mipmap;
|
||||
|
||||
varying vec4 v_angle_mult;
|
||||
|
||||
#include "Common.gdshaderinc"
|
||||
|
||||
// Atmosphere
|
||||
vec3 calc_atmospheric_scatter(float sr, float sm, vec2 mu, vec3 mult, float depth) {
|
||||
vec3 beta_mie = atm_beta_mie;
|
||||
vec3 beta_ray = atm_beta_ray * atm_thickness;
|
||||
|
||||
vec3 extc_factor = clamp_rgb_to_unit(exp(-(beta_ray * sr + beta_mie * sm)));
|
||||
|
||||
float extc_ff = mix(clamp_to_unit(atm_thickness * 0.5), 1.0, mult.x);
|
||||
vec3 final_extc_factor = mix(1.0 - extc_factor, (1.0 - extc_factor) * extc_factor, extc_ff);
|
||||
|
||||
float rayleigh_phase = calc_rayleigh_phase(mu.x);
|
||||
vec3 BRT = beta_ray * rayleigh_phase * clamp_to_unit(depth * fog_rayleigh_depth);
|
||||
vec3 BMT = beta_mie * calc_mie_phase(mu.x, atm_sun_partial_mie_phase);
|
||||
BMT *= atm_sun_mie_intensity * atm_sun_mie_tint.rgb * clamp_to_unit(depth * fog_mie_depth);
|
||||
|
||||
vec3 BRMT = (BRT + BMT) / (beta_ray + beta_mie);
|
||||
vec3 scatter = atm_sun_intensity * (BRMT * final_extc_factor) * atm_day_tint.rgb * mult.y;
|
||||
scatter = mix(scatter, scatter * (1.0 - extc_factor), atm_darkness);
|
||||
|
||||
vec3 lcol = mix(atm_day_tint.rgb, atm_horizon_light_tint.rgb, mult.x);
|
||||
vec3 nscatter = (1.0 - extc_factor) * atm_night_tint.rgb * clamp_to_unit(depth * fog_rayleigh_depth);
|
||||
nscatter += calc_mie_phase(mu.y, atm_moon_partial_mie_phase) *
|
||||
atm_moon_mie_tint.rgb * atm_moon_mie_intensity * 0.005 * clamp_to_unit(depth * fog_mie_depth);
|
||||
|
||||
return (scatter * lcol) + nscatter;
|
||||
}
|
||||
|
||||
// Fog
|
||||
float calc_fog_exp(float depth, float density) {
|
||||
return 1.0 - clamp_to_unit(exp2(-depth * density));
|
||||
}
|
||||
|
||||
float calc_fog_falloff(float y, float zeroLevel, float falloff) {
|
||||
return clamp_to_unit(exp(-(y + zeroLevel) * falloff));
|
||||
}
|
||||
|
||||
float calc_fog_distance(float depth) {
|
||||
float d = depth;
|
||||
d = (fog_end - d) / (fog_end - fog_start);
|
||||
return clamp_to_unit(1.0 - d);
|
||||
}
|
||||
|
||||
|
||||
// Main
|
||||
|
||||
void vertex() {
|
||||
POSITION = vec4(VERTEX.xy, 1.0, 1.0);
|
||||
v_angle_mult = vec4(
|
||||
clamp_to_unit(1.0 - sun_direction.y),
|
||||
clamp_to_unit(sun_direction.y + 0.45),
|
||||
clamp_to_unit(-sun_direction.y + 0.30),
|
||||
clamp_to_unit(-sun_direction.y + 0.60));
|
||||
}
|
||||
|
||||
void fragment() {
|
||||
// Calculate world pixel position from depth
|
||||
float depth_raw = texture(DEPTH_TEXTURE, SCREEN_UV).r;
|
||||
#ifndef CURRENT_RENDERER
|
||||
// For 4.3 or earlier, will break if far plane is less than 1m.
|
||||
depth_raw = PROJECTION_MATRIX[3][2] > PROJECTION_MATRIX[2][2] ? depth_raw : depth_raw * 2.0 - 1.0;
|
||||
#else
|
||||
#if CURRENT_RENDERER == RENDERER_COMPATIBILITY
|
||||
depth_raw = depth_raw * 2.0 - 1.0;
|
||||
#endif
|
||||
#endif
|
||||
vec3 ndc = vec3(SCREEN_UV * 2.0 - 1.0, depth_raw);
|
||||
vec4 clip_pos = INV_PROJECTION_MATRIX * vec4(ndc, 1.0);
|
||||
vec3 view_pos = clip_pos.xyz / clip_pos.w;
|
||||
vec4 world_pos_hom = INV_VIEW_MATRIX * vec4(view_pos, 1.0);
|
||||
vec3 world_position = world_pos_hom.xyz / world_pos_hom.w;
|
||||
|
||||
// Ray from camera to world intersection point
|
||||
vec3 intersection_ray = world_position - CAMERA_POSITION_WORLD;
|
||||
// Unit ray direction
|
||||
vec3 ray_direction = normalize(intersection_ray);
|
||||
//float linear_depth = -view_pos.z; // Approximate ray distance
|
||||
//float ray_distance = length(intersection_ray); // Exact ray distance
|
||||
|
||||
// Cap ray if there's a sea_level plane intersection
|
||||
float original_distance = length(intersection_ray);
|
||||
// Y difference from camera to sea_level
|
||||
float dy = sea_level - CAMERA_POSITION_WORLD.y;
|
||||
// Distance along ray to sea_level plane
|
||||
float plane_distance = dy / (ray_direction.y + 1e-6 * sign(ray_direction.y)); // avoid divide by zero
|
||||
float plane_hit = float(ray_direction.y < 0.) *
|
||||
float(plane_distance > 0.) *
|
||||
float(plane_distance < original_distance);
|
||||
|
||||
// Use final distance or t_plane distance Final distance: mix original vs plane distance
|
||||
float ray_distance = mix(original_distance, plane_distance, plane_hit);
|
||||
|
||||
// Capped world position
|
||||
//world_position = CAMERA_POSITION_WORLD + ray_distance * ray_direction;
|
||||
|
||||
float fog_factor = calc_fog_exp(ray_distance, fog_density);
|
||||
fog_factor *= calc_fog_falloff(ray_direction.y, 0.0, fog_falloff);
|
||||
fog_factor *= calc_fog_distance(ray_distance);
|
||||
|
||||
vec2 mu = vec2(
|
||||
dot(sun_direction, ray_direction),
|
||||
dot(moon_direction, ray_direction)
|
||||
);
|
||||
float sr, sm;
|
||||
calc_simple_optical_depth(ray_direction.y + atm_level_params.z, sr, sm, atm_level_params.xy);
|
||||
vec3 scatter = calc_atmospheric_scatter(sr, sm, mu, v_angle_mult.xyz, ray_distance);
|
||||
|
||||
vec4 fog_color = vec4(scatter, fog_factor);
|
||||
fog_color.a = clamp(fog_color.a, 0.0, 1.0);
|
||||
fog_color.rgb = apply_photo_tonemap(fog_color.rgb, color_correction.y, color_correction.x);
|
||||
|
||||
ALBEDO = fog_color.rgb;
|
||||
ALPHA = fog_color.a;
|
||||
|
||||
// View depth map
|
||||
//ALPHA = 1.;
|
||||
//ALBEDO = vec3(ray_distance*.0001);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
uid://bqaetpq827vg6
|
||||
@@ -0,0 +1,64 @@
|
||||
// Copyright (c) 2023-2025 Cory Petkovsek and Contributors
|
||||
// Copyright (c) 2021 J. Cuellar
|
||||
|
||||
const float INV_PI = 1. / PI;
|
||||
const float HALF_PI = PI / 2.;
|
||||
const float INV_HALF_PI = 1. / HALF_PI;
|
||||
const float QRT_PI = PI / 4.;
|
||||
const float INV_QRT_PI = 1. / QRT_PI;
|
||||
const float PI4 = PI * 4.;
|
||||
const float INV_PI4 = 1. / PI4;
|
||||
const float INV_TAU = 1. / TAU;
|
||||
|
||||
const float RAYLEIGH_ZENITH_LENGTH = 8.4e3;
|
||||
const float MIE_ZENITH_LENGTH = 1.25e3;
|
||||
const float THREE_OVER_EIGHT_PI = 3.0 / (8.0 * PI);
|
||||
|
||||
|
||||
float clamp_to_unit(float value) {
|
||||
return clamp(value, 0.0, 1.0);
|
||||
}
|
||||
|
||||
vec3 clamp_rgb_to_unit(vec3 value) {
|
||||
return clamp(value.rgb, 0.0, 1.0);
|
||||
}
|
||||
|
||||
vec3 adjust_contrast(vec3 vec, float level) {
|
||||
return mix(vec, vec * vec * vec, level); // pow3
|
||||
}
|
||||
|
||||
vec3 apply_photo_tonemap(vec3 color, float exposure, float level) {
|
||||
color.rgb *= exposure;
|
||||
return mix(color.rgb, 1.0 - exp(-color.rgb), level);
|
||||
}
|
||||
|
||||
float calc_rayleigh_phase(float mu) {
|
||||
return THREE_OVER_EIGHT_PI * (1.0 + mu * mu);
|
||||
}
|
||||
|
||||
float calc_mie_phase(float mu, vec3 partial) {
|
||||
float denominator = partial.y - partial.z * mu;
|
||||
float safe_denominator = max(denominator, 0.0001); // Prevent division by zero on negative powers
|
||||
return PI4 * partial.x * pow(safe_denominator, -1.5);
|
||||
}
|
||||
|
||||
// Simplifield for more performance
|
||||
void calc_simple_optical_depth(float y, out float sr, out float sm, vec2 params) {
|
||||
y = max(0.03, y + 0.03) + params.y;
|
||||
y = 1.0 / (y * params.x);
|
||||
sr = y * RAYLEIGH_ZENITH_LENGTH;
|
||||
sm = y * MIE_ZENITH_LENGTH;
|
||||
}
|
||||
|
||||
// Paper based (Unused)
|
||||
void calc_optical_depth(float y, out float sr, out float sm, vec2 params) {
|
||||
y = max(0.0, y);
|
||||
y = clamp_to_unit(y * params.x);
|
||||
|
||||
float zenith = acos(y);
|
||||
zenith = cos(zenith) + 0.15 * pow(93.885 - ((zenith * 180.0) * INV_PI), -1.253);
|
||||
zenith = 1.0 / (zenith + params.y);
|
||||
|
||||
sr = zenith * RAYLEIGH_ZENITH_LENGTH;
|
||||
sm = zenith * MIE_ZENITH_LENGTH;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
uid://0auvnvj85575
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 J. Cuéllar
|
||||
|
||||
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.
|
||||
@@ -0,0 +1,517 @@
|
||||
// Copyright (c) 2023-2025 Cory Petkovsek and Contributors
|
||||
// Copyright (c) 2021 J. Cuellar
|
||||
|
||||
shader_type sky;
|
||||
render_mode use_debanding;
|
||||
|
||||
group_uniforms general;
|
||||
uniform bool sky_visible = true;
|
||||
uniform vec2 color_correction = vec2(0.0, 1.0);
|
||||
uniform vec4 ground_color: source_color = vec4(0.3, 0.3, 0.3, 1.0);
|
||||
uniform float horizon_offset = 0.0;
|
||||
|
||||
group_uniforms atmosphere;
|
||||
uniform float atm_darkness = 0.5;
|
||||
uniform float atm_sun_intensity = 18.0;
|
||||
uniform vec4 atm_day_tint: source_color = vec4(0.808, 0.91, 1.0, 1.0);
|
||||
uniform vec4 atm_horizon_light_tint: source_color = vec4(0.98, 0.635, 0.463, 1.0);
|
||||
uniform vec4 atm_night_tint: source_color = vec4 (0.041, 0.049, 0.061, 0.245);
|
||||
uniform vec3 atm_level_params = vec3(1.0, 0.0, 0.0);
|
||||
uniform float atm_thickness = 0.7;
|
||||
uniform vec3 atm_beta_ray = vec3(0.00001, 0.00001, 0.00003);
|
||||
uniform vec3 atm_beta_mie = vec3(0.00000003038); // Value computed by multiplying Mie, turbidity, and 0.000434.
|
||||
|
||||
group_uniforms sun_disk;
|
||||
uniform vec4 sun_light_color: source_color = vec4(1.0);
|
||||
uniform vec4 sun_disk_color: source_color = vec4(0.996, 0.541, 0.14, 1.0);
|
||||
uniform float sun_disk_intensity = 2.0;
|
||||
uniform float sun_disk_size = 0.015;
|
||||
|
||||
group_uniforms sun_mie;
|
||||
uniform vec4 atm_sun_mie_tint: source_color = vec4(1.0);
|
||||
uniform float atm_sun_mie_intensity = 1.0;
|
||||
uniform vec3 atm_sun_partial_mie_phase = vec3(0.36, 1.64, 1.6);
|
||||
|
||||
group_uniforms moon;
|
||||
uniform vec4 moon_color: source_color = vec4(1.0);
|
||||
uniform sampler2D moon_texture: source_color, repeat_disable;
|
||||
uniform vec3 moon_texture_alignment = vec3(7.0, 1.4, 4.8);
|
||||
uniform bool moon_texture_flip_u;
|
||||
uniform bool moon_texture_flip_v;
|
||||
uniform float moon_size = 0.07;
|
||||
|
||||
group_uniforms moon_mie;
|
||||
uniform vec4 atm_moon_mie_tint: source_color = vec4(0.137, 0.184, 0.292, 1.0);
|
||||
uniform float atm_moon_mie_intensity = 0.17152;
|
||||
uniform vec3 atm_moon_partial_mie_phase = vec3(0.36, 1.64, 1.6);
|
||||
|
||||
group_uniforms star_map;
|
||||
uniform vec4 starmap_color: source_color = vec4(0.71, 0.71, 0.71, 0.855);
|
||||
uniform sampler2D starmap_texture: source_color;
|
||||
uniform bool starmap_flip_u = false;
|
||||
uniform bool starmap_flip_v = false;
|
||||
uniform vec3 starmap_alignment = vec3(2.68288, -0.25891, 0.40101);
|
||||
uniform float star_rotation_offset = 9.38899;
|
||||
uniform float star_rotation = 0.0; // For simulating Earth rotation.
|
||||
uniform float star_tilt = 0.0; // For tilting the sky according to observer latitude in radians. (90° - latitude)
|
||||
|
||||
group_uniforms star_field;
|
||||
uniform vec4 star_field_color: source_color = vec4(1.0);
|
||||
uniform sampler2D star_field_texture: source_color;
|
||||
uniform float star_scintillation = 0.75;
|
||||
uniform float star_scintillation_speed = 0.01;
|
||||
uniform sampler2D noise_tex: source_color;
|
||||
|
||||
/* Sky alignment values for a star map that is in galactic coordinates. Works for most star maps.
|
||||
X: 2.6555
|
||||
Y: -0.23935
|
||||
Z: 0.4505
|
||||
|
||||
These alignment values are consistent with a sky view from the North Pole (latitude zero)
|
||||
at midnight. The positions of Polaris, Vega, Rigel, and Alpha Centauri confirmed
|
||||
through various sources and calculations.
|
||||
*/
|
||||
|
||||
group_uniforms clouds;
|
||||
uniform vec4 clouds_night_color: source_color = vec4(0.09, 0.094, 0.129, 1.0);
|
||||
|
||||
group_uniforms cirrus_clouds;
|
||||
uniform bool cirrus_visible = true;
|
||||
uniform float cirrus_intensity = 2.0;
|
||||
uniform float cirrus_coverage = 0.5;
|
||||
uniform float cirrus_thickness = 10.0;
|
||||
uniform float cirrus_absorption = 2.0;
|
||||
uniform float cirrus_sky_tint_fade = 0.5;
|
||||
uniform float cirrus_size = 1.0;
|
||||
uniform vec2 cirrus_uv = vec2(0.16, 0.11);
|
||||
uniform vec2 cirrus_position1 = vec2(0.0);
|
||||
uniform vec2 cirrus_position2 = vec2(0.0);
|
||||
uniform sampler2D cirrus_texture;
|
||||
|
||||
group_uniforms cumulus_clouds;
|
||||
uniform bool cumulus_visible = true;
|
||||
uniform float cumulus_intensity = 0.6;
|
||||
uniform float cumulus_coverage = 0.55;
|
||||
uniform float cumulus_thickness = 0.0243;
|
||||
uniform float cumulus_absorption = 2.0;
|
||||
uniform float cumulus_noise_freq = 2.7;
|
||||
uniform float cumulus_sky_tint_fade = 0.0;
|
||||
uniform float cumulus_size = 0.5;
|
||||
uniform vec2 cumulus_position = vec2(0.0);
|
||||
uniform sampler2D cumulus_texture;
|
||||
uniform vec3 cumulus_partial_mie_phase = vec3(0.95756, 1.04244, 0.412);
|
||||
uniform float cumulus_mie_intensity = 1.0;
|
||||
|
||||
group_uniforms overlays;
|
||||
uniform bool show_azimuthal_grid = false;
|
||||
uniform vec4 azimuthal_grid_color : source_color = vec4(.871, .722, .529, 1.);
|
||||
uniform float azimuthal_grid_rotation_offset = 0.03;
|
||||
uniform bool show_equatorial_grid = false;
|
||||
uniform vec4 equatorial_grid_color : source_color = vec4(0., .75, 1., 1.);
|
||||
uniform float equatorial_grid_rotation_offset = 0.03;
|
||||
|
||||
group_uniforms transform_matrices;
|
||||
uniform mat3 moon_matrix; // Calculated externally via GDScript.
|
||||
|
||||
#include "Common.gdshaderinc"
|
||||
|
||||
// Provides a transformation matrix when passed a vec3.
|
||||
mat3 create_rotation_matrix(vec3 r) {
|
||||
float c1 = cos(r.x), s1 = sin(r.x);
|
||||
float c2 = cos(r.y), s2 = sin(r.y);
|
||||
float c3 = cos(r.z), s3 = sin(r.z);
|
||||
|
||||
mat3 rotX = mat3(
|
||||
vec3(1.0, 0.0, 0.0),
|
||||
vec3(0.0, c1, -s1),
|
||||
vec3(0.0, s1, c1)
|
||||
);
|
||||
|
||||
mat3 rotY = mat3(
|
||||
vec3(c2, 0.0, s2),
|
||||
vec3(0.0, 1.0, 0.0),
|
||||
vec3(-s2, 0.0, c2)
|
||||
);
|
||||
|
||||
mat3 rotZ = mat3(
|
||||
vec3(c3, -s3, 0.0),
|
||||
vec3(s3, c3, 0.0),
|
||||
vec3(0.0, 0.0, 1.0)
|
||||
);
|
||||
|
||||
return rotZ * rotY * rotX; // Combine rotations in ZYX order
|
||||
}
|
||||
|
||||
vec2 calc_equirect_uv(vec3 norm) {
|
||||
float flipu = starmap_flip_u ? -1.0 : 1.0;
|
||||
float flipv = starmap_flip_v ? -1.0 : 1.0;
|
||||
float u = flipu * ((atan(norm.y, norm.x) + PI) * INV_TAU); // 0 to 1 horizontally
|
||||
float v = -flipv * (acos(norm.z) * INV_PI); // 0 (north pole) to 1 (south pole)
|
||||
return vec2(u, v);
|
||||
}
|
||||
|
||||
vec3 sample_moon_texture(vec3 dir) {
|
||||
dir = moon_matrix * dir * create_rotation_matrix(moon_texture_alignment);
|
||||
dir.x = -dir.x;
|
||||
dir.z = -dir.z;
|
||||
float flipu = moon_texture_flip_u ? 1.0 : -1.0;
|
||||
float flipv = moon_texture_flip_v ? 1.0 : -1.0;
|
||||
float u = 0.5 + flipu * atan(dir.z, dir.x) * INV_TAU; // Longitude
|
||||
float v = 0.5 + flipv * asin(dir.y) * INV_PI; // Latitude
|
||||
return texture(moon_texture, vec2(u, v)).rgb;
|
||||
}
|
||||
|
||||
float calc_disk_mask(vec3 norm, vec3 coords, lowp float size) {
|
||||
float dist = length(norm - coords);
|
||||
return 1.0 - step(size, dist);
|
||||
}
|
||||
|
||||
float map_range(float value, float from_min, float from_max, float to_min, float to_max) {
|
||||
return to_min + (value - from_min) * (to_max - to_min) / (from_max - from_min);
|
||||
}
|
||||
|
||||
bool ray_sphere_intersect(float r, vec3 origin, vec3 dir, out float t, out vec3 nrm) {
|
||||
origin += vec3(0.0, 450.0, 0.0);
|
||||
float a = dot(dir, dir);
|
||||
float b = 2.0 * dot(origin, dir);
|
||||
float c = dot(origin, origin) - r * r;
|
||||
float d = b * b - 4.0 * a * c;
|
||||
float valid = step(0.0, d); // 1.0 if d >= 0.0, else 0.0
|
||||
d = sqrt(max(d, 0.0)); // Prevent sqrt of negative numbers
|
||||
a *= 2.0;
|
||||
float t1 = 0.5 * (-b + d);
|
||||
float t2 = 0.5 * (-b - d);
|
||||
float t_min = min(t1, t2); // Select the smallest intersection
|
||||
t = mix(t2, t1, step(t2, t1) * step(0.0, t1)); // Use t1 if t2 < t1 and t1 >= 0, else t2
|
||||
t = mix(0.0, t, step(0.0, t) * valid); // Invalidate if t < 0 or d < 0
|
||||
nrm = mix(vec3(0.0), origin + t * dir, step(0.0, t) * valid); // Set nrm only if valid
|
||||
return bool(step(0.0, t) * valid);
|
||||
}
|
||||
|
||||
// For rendering the moon. This function courtesy of Kelvin van Hoorn.
|
||||
// https://kelvinvanhoorn.com/2024/12/07/skybox-tutorial-godot/
|
||||
float simple_sphere_intersect(vec3 ray_dir, vec3 sphere_pos, float radius) {
|
||||
vec3 oc = -sphere_pos;
|
||||
float b = dot(oc, ray_dir);
|
||||
float c = dot(oc, oc) - radius * radius;
|
||||
float h = b * b - c;
|
||||
float valid = step(0.0, h); // 1.0 if h >= 0.0, else 0.0
|
||||
h = sqrt(max(h, 0.0)); // Ensure sqrt is non-negative
|
||||
return mix(-1.0, -b - h, valid);
|
||||
}
|
||||
|
||||
// Atmosphere
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
vec3 calc_atmospheric_scatter(float sr, float sm, vec2 mu, vec3 mult) {
|
||||
vec3 beta_mie = atm_beta_mie;
|
||||
vec3 beta_ray = atm_beta_ray * atm_thickness;
|
||||
|
||||
vec3 extc_factor = clamp_rgb_to_unit(exp(-(beta_ray * sr + beta_mie * sm)));
|
||||
|
||||
float extc_ff = mix(clamp_to_unit(atm_thickness * 0.5), 1.0, mult.x);
|
||||
vec3 final_extc_factor = mix(1.0 - extc_factor, (1.0 - extc_factor) * extc_factor, extc_ff);
|
||||
float rayleigh_phase = calc_rayleigh_phase(mu.x);
|
||||
vec3 BRT = beta_ray * rayleigh_phase;
|
||||
vec3 BMT = beta_mie * calc_mie_phase(mu.x, atm_sun_partial_mie_phase);
|
||||
BMT *= atm_sun_mie_intensity * atm_sun_mie_tint.rgb;
|
||||
|
||||
vec3 BRMT = (BRT + BMT) / (beta_ray + beta_mie);
|
||||
vec3 scatter = atm_sun_intensity * (BRMT * final_extc_factor) * atm_day_tint.rgb * mult.y;
|
||||
scatter = mix(scatter, scatter * (1.0 - extc_factor), atm_darkness);
|
||||
|
||||
vec3 lcol = mix(atm_day_tint.rgb, atm_horizon_light_tint.rgb, mult.x);
|
||||
vec3 nscatter = (1.0 - extc_factor) * atm_night_tint.rgb;
|
||||
nscatter += calc_mie_phase(mu.y, atm_moon_partial_mie_phase) *
|
||||
atm_moon_mie_tint.rgb * atm_moon_mie_intensity * 0.005;
|
||||
|
||||
return (scatter * lcol) + nscatter;
|
||||
}
|
||||
|
||||
// Clouds
|
||||
//------------------------------------------------------------------------------
|
||||
const int CUMULUS_STEP = 10;
|
||||
|
||||
float sample_cirrus_noise(vec2 coords) {
|
||||
float a = textureLod(cirrus_texture, coords.xy * cirrus_uv + cirrus_position1, 0.0).r;
|
||||
float b = textureLod(cirrus_texture, coords.xy * cirrus_uv + cirrus_position2, 0.0).r;
|
||||
return ((a + b) * 0.5);
|
||||
}
|
||||
|
||||
float sample_cumulus_noise(vec3 p) {
|
||||
vec3 pos = vec3(p * 0.01);
|
||||
pos.z *= 256.0;
|
||||
vec2 offset = vec2(0.317, 0.123);
|
||||
vec4 uv = vec4(0.0);
|
||||
uv.xy = pos.xy + offset * floor(pos.z);
|
||||
uv.zw = uv.xy + offset;
|
||||
float x1 = textureLod(cumulus_texture, uv.xy, 0.0).r;
|
||||
float x2 = textureLod(cumulus_texture, uv.zw, 0.0).r;
|
||||
return mix(x1, x2, fract(pos.z));
|
||||
}
|
||||
|
||||
float calc_cumulus_fbm(vec3 p, float l) {
|
||||
float ret;
|
||||
ret = 0.51749673 * sample_cumulus_noise(p);
|
||||
p *= l;
|
||||
ret += 0.25584929 * sample_cumulus_noise(p);
|
||||
p *= l;
|
||||
ret += 0.12527603 * sample_cumulus_noise(p);
|
||||
p *= l;
|
||||
ret += 0.06255931 * sample_cumulus_noise(p);
|
||||
return ret;
|
||||
}
|
||||
|
||||
float calc_cirrus_density(vec2 p) {
|
||||
float d = sample_cirrus_noise(p);
|
||||
float c = 1.0 - cirrus_coverage;
|
||||
d = d - c;
|
||||
return clamp_to_unit(d);
|
||||
}
|
||||
|
||||
float calc_cumulus_density(vec3 p, vec3 offset, float t) {
|
||||
vec3 pos = p * 0.0212242 - offset;
|
||||
float dens = calc_cumulus_fbm(pos, cumulus_noise_freq);
|
||||
dens += dens;
|
||||
|
||||
float cov = 1.0 - cumulus_coverage;
|
||||
cov = smoothstep(0.00, (cov * 3.5) + t, dens);
|
||||
dens *= cov;
|
||||
dens = map_range(dens, 1.0 - cov, 1.0, 0.0, 1.0);
|
||||
|
||||
return clamp_to_unit(dens);
|
||||
}
|
||||
|
||||
vec4 render_cirrus_clouds(vec3 pos) {
|
||||
pos.xy = pos.xz / pos.y;
|
||||
pos *= cirrus_size;
|
||||
float density = calc_cirrus_density(pos.xy);
|
||||
float sh = clamp_to_unit(exp(-cirrus_absorption * density));
|
||||
float a = clamp_to_unit(density * cirrus_thickness);
|
||||
return vec4(vec3(density*sh) * cirrus_intensity, a);
|
||||
}
|
||||
|
||||
vec4 render_cumulus_clouds(vec3 ro, vec3 rd, float am, vec3 sun_pos, vec3 moon_pos) {
|
||||
vec4 ret = vec4(0.);
|
||||
vec3 wind = -vec3(cumulus_position.x, 0.0, cumulus_position.y);
|
||||
float a = 0.0;
|
||||
|
||||
// n and tt doesnt need to be initialized since it would be set by ray_sphere_intersect
|
||||
vec3 n; float tt;
|
||||
if (ray_sphere_intersect(500., ro, rd, tt, n)) {
|
||||
float march_step = float(CUMULUS_STEP) * cumulus_thickness;
|
||||
vec3 dir_step = rd / rd.y * march_step;
|
||||
vec3 pos = n * cumulus_size;
|
||||
|
||||
vec2 mu = vec2(dot(sun_pos, rd), dot(moon_pos, rd));
|
||||
vec3 mph = ((calc_mie_phase(mu.x, cumulus_partial_mie_phase) * atm_sun_mie_tint.rgb) +
|
||||
calc_mie_phase(mu.y, cumulus_partial_mie_phase) * am);
|
||||
|
||||
vec4 t = vec4(1.0);
|
||||
t.rgb += (mph.rgb * cumulus_mie_intensity);
|
||||
|
||||
for(int i = 0; i < CUMULUS_STEP; i++) {
|
||||
float h = float(i) * 0.1; // / float(kCLOUDS_STEP);
|
||||
float density = calc_cumulus_density(pos, wind, h);
|
||||
float sh = clamp_to_unit(exp(-cumulus_absorption * density * march_step));
|
||||
t *= sh;
|
||||
ret += (t * (exp(h) * 0.571428571) * density * march_step);
|
||||
a += (1.0 - sh) * (1.0 - a);
|
||||
pos += dir_step;
|
||||
}
|
||||
return vec4(ret.rgb * cumulus_intensity, a);
|
||||
}
|
||||
return vec4(ret.rgb * cumulus_intensity, a);
|
||||
}
|
||||
|
||||
// Main
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
vec3 render_sky(vec3 world_pos, vec3 clouds_pos, vec3 sun_pos, vec3 moon_pos, float p_time) {
|
||||
vec3 col = vec3(0.0);
|
||||
|
||||
vec4 angle_mult;
|
||||
angle_mult.x = clamp_to_unit(1.0 - sun_pos.y);
|
||||
angle_mult.y = clamp_to_unit(sun_pos.y + 0.45);
|
||||
angle_mult.z = clamp_to_unit(-sun_pos.y + 0.30);
|
||||
angle_mult.w = clamp_to_unit(-sun_pos.y + 0.60);
|
||||
|
||||
// Atmosphere
|
||||
vec2 mu = vec2(dot(sun_pos, world_pos), dot(moon_pos, world_pos));
|
||||
float sr, sm;
|
||||
calc_simple_optical_depth(world_pos.y + atm_level_params.z - horizon_offset, sr, sm, atm_level_params.xy);
|
||||
|
||||
world_pos.y -= horizon_offset;
|
||||
float horizonBlend = clamp_to_unit((world_pos.y - 0.03) * 3.0);
|
||||
float horizonBlendCumulus = clamp_to_unit((world_pos.y + 0.01) * 50.0);
|
||||
|
||||
vec3 scatter = calc_atmospheric_scatter(sr, sm, mu.xy, angle_mult.xyz);
|
||||
col.rgb += scatter.rgb;
|
||||
|
||||
// Sun
|
||||
vec3 sun_disk = calc_disk_mask(world_pos, sun_pos, sun_disk_size) * sun_disk_color.rgb * scatter.rgb;
|
||||
sun_disk *= sun_disk_intensity;
|
||||
|
||||
// Moon
|
||||
float moon_intersect = simple_sphere_intersect(world_pos, moon_pos, moon_size);
|
||||
float moon_mask = moon_intersect > (-1. + sqrt(moon_size)) ? 1.0 : 0.0;
|
||||
vec3 moon_normal = normalize(world_pos * moon_intersect - moon_pos);
|
||||
|
||||
float moon_ndotl = clamp(dot(moon_normal, sun_pos), 0.0, 1.0);
|
||||
vec3 moon_tex = sample_moon_texture(moon_normal);
|
||||
vec3 moon_output = moon_mask * moon_ndotl * exp2(1.0) * moon_tex * moon_color.rgb;
|
||||
float moonMask = (1.0 - moon_mask);
|
||||
|
||||
// Deep space
|
||||
|
||||
// Tilt the sky along the X-axis.
|
||||
mat3 tilt = create_rotation_matrix(vec3(star_tilt, 0.0, 0.0));
|
||||
|
||||
// Rotation around Y-axis. For simulating Earth's rotation.
|
||||
mat3 rotation = create_rotation_matrix(vec3(0.0, star_rotation + star_rotation_offset, 0.0));
|
||||
|
||||
// Initial XYZ rotation for alignment.
|
||||
mat3 alignment_xyz = create_rotation_matrix(starmap_alignment);
|
||||
|
||||
// Combine everything together to get the desired alignment.
|
||||
vec3 final_alignment = normalize(alignment_xyz * rotation * tilt * world_pos);
|
||||
|
||||
// Now generate UV coordinates for the texture.
|
||||
vec2 deep_space_uv = calc_equirect_uv(normalize(final_alignment));
|
||||
|
||||
// Star map
|
||||
vec3 deep_space_background = textureLod(starmap_texture, deep_space_uv, 0.0).rgb;
|
||||
deep_space_background *= starmap_color.rgb;
|
||||
deep_space_background = adjust_contrast(deep_space_background, starmap_color.a);
|
||||
vec3 deep_space = deep_space_background.rgb * moonMask + moon_output;
|
||||
|
||||
// Star Field
|
||||
float star_scint = textureLod(noise_tex, deep_space_uv + (p_time * star_scintillation_speed), 0.0).r;
|
||||
star_scint = mix(1.0, star_scint * 1.5, star_scintillation);
|
||||
|
||||
vec3 star_field = textureLod(star_field_texture, deep_space_uv, 0.0).rgb * star_field_color.rgb;
|
||||
star_field = clamp_rgb_to_unit(mix(star_field.rgb, star_field.rgb * star_scint, star_scintillation));
|
||||
star_field = clamp_rgb_to_unit(star_field.rgb);
|
||||
//deep_space.rgb -= clamp_to_unit(star_field.r * 10.0);
|
||||
deep_space.rgb += star_field.rgb * moonMask;
|
||||
deep_space.rgb *= angle_mult.z;
|
||||
col.rgb += sun_disk.rgb + deep_space.rgb * horizonBlend;
|
||||
|
||||
// Clouds
|
||||
if (cirrus_visible) {
|
||||
vec4 cirrus_clouds = render_cirrus_clouds(clouds_pos);
|
||||
cirrus_clouds.a = clamp_to_unit(cirrus_clouds.a);
|
||||
vec3 cloud_tint = sun_light_color.rgb;
|
||||
cloud_tint = mix(cloud_tint, clouds_night_color.rgb, angle_mult.w);
|
||||
cirrus_clouds.rgb *= cloud_tint * cirrus_intensity;
|
||||
cirrus_clouds.a = mix(0.0, cirrus_clouds.a, horizonBlend);
|
||||
col.rgb = mix(col.rgb, cirrus_clouds.rgb + mix(vec3(0.0), scatter, cirrus_sky_tint_fade), cirrus_clouds.a);
|
||||
}
|
||||
|
||||
if (cumulus_visible) {
|
||||
vec4 cumulus_clouds = render_cumulus_clouds(vec3(0.0), clouds_pos, angle_mult.z, sun_pos, moon_pos);
|
||||
cumulus_clouds.a = clamp_to_unit(cumulus_clouds.a);
|
||||
vec3 cloud_tint = sun_light_color.rgb;
|
||||
cloud_tint = mix(cloud_tint, clouds_night_color.rgb, angle_mult.w);
|
||||
cumulus_clouds.rgb *= cloud_tint * cumulus_intensity;
|
||||
cumulus_clouds.a = mix(0.0, cumulus_clouds.a, horizonBlend);
|
||||
col.rgb = mix(col.rgb, cumulus_clouds.rgb + mix(vec3(0.0), scatter, cumulus_sky_tint_fade), cumulus_clouds.a);
|
||||
}
|
||||
|
||||
col.rgb = mix(col.rgb, ground_color.rgb * scatter, clamp_to_unit((-world_pos.y - atm_level_params.z) * 100.0));
|
||||
col.rgb = apply_photo_tonemap(col.rgb, color_correction.y, color_correction.x);
|
||||
return col;
|
||||
}
|
||||
|
||||
vec4 create_axis_angle_quat(vec3 axis, float angle) {
|
||||
float s = sin(angle * 0.5);
|
||||
return vec4(axis * s, cos(angle * 0.5));
|
||||
}
|
||||
|
||||
vec3 rotate_vector_by_quat(vec3 v, vec4 q) {
|
||||
vec3 t = 2.0 * cross(q.xyz, v);
|
||||
return v + q.w * t + cross(q.xyz, t);
|
||||
}
|
||||
|
||||
vec3 align_and_rotate_vector(vec3 dir, vec3 target, float rotation_angle) {
|
||||
vec3 z_axis = vec3(0.0, 0.0, 1.0); // Original grid alignment
|
||||
|
||||
// First, align Z-axis with target (Polaris)
|
||||
target = normalize(target);
|
||||
vec3 axis = cross(z_axis, target);
|
||||
float angle = acos(dot(z_axis, target));
|
||||
float valid = step(0.0001, length(axis)); // 1.0 if length(axis) >= 0.0001
|
||||
axis = normalize(axis);
|
||||
vec4 align_quat = create_axis_angle_quat(axis, angle);
|
||||
dir = mix(dir, rotate_vector_by_quat(dir, align_quat), valid);
|
||||
vec4 rot_quat = create_axis_angle_quat(vec3(0.0, 0.0, 1.0), rotation_angle);
|
||||
dir = mix(dir, rotate_vector_by_quat(dir, rot_quat), valid);
|
||||
return dir;
|
||||
}
|
||||
|
||||
void render_grid(vec3 dir, vec3 pole_dir, float rotation_angle, vec4 line_color, float grid_spacing, float line_thickness, inout vec3 color) {
|
||||
float spacing = max(grid_spacing, 0.001);
|
||||
float thickness = max(line_thickness, 0.0001);
|
||||
|
||||
// Rotate dir with sidereal motion and align with pole_dir
|
||||
dir = align_and_rotate_vector(dir, pole_dir, rotation_angle);
|
||||
|
||||
// Latitude / Circles / Theta Spherical coordinates
|
||||
float theta = acos(dir.z);
|
||||
float theta_deriv = fwidth(theta);
|
||||
float circle_thickness = max(thickness, theta_deriv);
|
||||
float circle = mod(theta, spacing);
|
||||
float circle_line = 1.0 - smoothstep(0.0, circle_thickness * 1.5, abs(circle - circle_thickness * 0.5));
|
||||
circle_line *= clamp_to_unit(thickness / circle_thickness);
|
||||
|
||||
// Add poles
|
||||
float pole_thickness = circle_thickness;
|
||||
float north_pole = 1.0 - smoothstep(0.0, pole_thickness, theta);
|
||||
float south_pole = 1.0 - smoothstep(0.0, pole_thickness, abs(theta - PI));
|
||||
circle_line = max(circle_line, max(north_pole, south_pole));
|
||||
|
||||
// Darken Lat at poles
|
||||
float circle_adjust = sin(theta);
|
||||
circle_line *= clamp(circle_adjust, 0.2, 1.0);
|
||||
|
||||
// Longitude / Radial lines / Phi Spherical coordinates
|
||||
float phi = atan(dir.y, dir.x);
|
||||
float phi_deriv = fwidth(phi);
|
||||
float radial_thickness = max(thickness, phi_deriv);
|
||||
float radial = mod(phi + PI, spacing); // PI w/ less precision to remove moire
|
||||
float radial_line = 1.0 - smoothstep(0.0, radial_thickness * 1.5, abs(radial - radial_thickness));
|
||||
radial_line *= clamp_to_unit(thickness / radial_thickness);
|
||||
|
||||
float grid = max(radial_line, circle_line);
|
||||
color += line_color.rgb * line_color.a * grid;
|
||||
}
|
||||
|
||||
void sky() {
|
||||
float current_time = TIME;
|
||||
vec3 col = vec3(0.0); // Set up the initial sky color which will be added to later to produce the final result.
|
||||
vec3 world_pos = EYEDIR; // Original shader set this up in the vertex() function. For sky shaders the overall equivalent is EYEDIR.
|
||||
vec3 clouds_pos = EYEDIR; // The same is used for cloud rendering.
|
||||
vec3 sun_pos = LIGHT0_DIRECTION; // Redefined from now-deprecated _sun_direction.
|
||||
vec3 moon_pos = LIGHT1_DIRECTION; // Redefined from now-deprecated _moon_direction.
|
||||
|
||||
if (sky_visible) {
|
||||
col = render_sky(world_pos, clouds_pos, sun_pos, moon_pos, current_time);
|
||||
}
|
||||
|
||||
// Draw overlay grids
|
||||
{
|
||||
float grid_thickness = 0.0002;
|
||||
float grid_spacing = 0.261799; // 15 degrees
|
||||
vec3 zenith_dir = vec3(0.0, 1.0, 0.0); // Y-axis aligned azimuthal
|
||||
vec3 north_celestial_pole = create_rotation_matrix(vec3(star_tilt, 0.0, 0.0)) * zenith_dir;
|
||||
float rotation_angle = star_rotation + star_rotation_offset;
|
||||
if (show_azimuthal_grid) {
|
||||
render_grid(EYEDIR, zenith_dir, azimuthal_grid_rotation_offset, azimuthal_grid_color, grid_spacing, grid_thickness, col);
|
||||
}
|
||||
if (show_equatorial_grid) {
|
||||
render_grid(EYEDIR, north_celestial_pole, rotation_angle + equatorial_grid_rotation_offset, equatorial_grid_color, grid_spacing, grid_thickness, col);
|
||||
}
|
||||
}
|
||||
|
||||
COLOR = col.rgb;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
uid://cvd0x7lon1ur6
|
||||
@@ -0,0 +1,31 @@
|
||||
# Copyright (c) 2023-2025 Cory Petkovsek and Contributors
|
||||
# Copyright (c) 2021 J. Cuellar
|
||||
|
||||
class_name OrbitalElements
|
||||
|
||||
# Handles Orbital elements for planetary system
|
||||
|
||||
var N: float # Longitude of the ascending node.
|
||||
var i: float # The Inclination to the ecliptic.
|
||||
var w: float # Argument of perihelion.
|
||||
var a: float # Semi-major axis, or mean distance from sun.
|
||||
var e: float # Eccentricity.
|
||||
var M: float # Mean anomaly.
|
||||
|
||||
|
||||
func get_orbital_elements(index: int, timeScale: float) -> void:
|
||||
if index == 0: # Sun
|
||||
N = 0.0
|
||||
i = 0.0
|
||||
w = 282.9404 + 4.70935e-5 * timeScale
|
||||
a = 0.0
|
||||
e = 0.016709 - 1.151e-9 * timeScale
|
||||
M = 356.0470 + 0.9856002585 * timeScale
|
||||
else: # Moon
|
||||
N = 125.1228 - 0.0529538083 * timeScale
|
||||
i = 5.1454
|
||||
w = 318.0634 + 0.1643573223 * timeScale
|
||||
a = 60.2666
|
||||
e = 0.054900
|
||||
M = 115.3654 + 13.0649929509 * timeScale
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
uid://4ka6iw74pja3
|
||||
@@ -0,0 +1,24 @@
|
||||
# Copyright (c) 2023-2025 Cory Petkovsek and Contributors
|
||||
# Copyright (c) 2021 J. Cuellar
|
||||
|
||||
@tool
|
||||
extends EditorPlugin
|
||||
|
||||
const __sky3d_script: String = "res://addons/sky_3d/src/Sky3D.gd"
|
||||
const __sky3d_icon: String = "res://addons/sky_3d/assets/textures/SkyIcon.png"
|
||||
const __skydome_script: String = "res://addons/sky_3d/src/SkyDome.gd"
|
||||
const __skydome_icon: String = "res://addons/sky_3d/assets/textures/SkyIcon.png"
|
||||
const __time_of_day_script: String = "res://addons/sky_3d/src/TimeOfDay.gd"
|
||||
const __time_of_day_icon: String = "res://addons/sky_3d/assets/textures/SkyIcon.png"
|
||||
|
||||
|
||||
func _enter_tree() -> void:
|
||||
add_custom_type("Sky3D", "Node", load(__sky3d_script), load(__sky3d_icon))
|
||||
add_custom_type("SkyDome", "Node", load(__skydome_script), load(__skydome_icon))
|
||||
add_custom_type("TimeOfDay", "Node", load(__time_of_day_script), load(__time_of_day_icon))
|
||||
|
||||
|
||||
func _exit_tree() -> void:
|
||||
remove_custom_type("Sky3D")
|
||||
remove_custom_type("SkyDome")
|
||||
remove_custom_type("TimeOfDay")
|
||||
@@ -0,0 +1 @@
|
||||
uid://ri5mkwojuh33
|
||||
@@ -0,0 +1,50 @@
|
||||
# Copyright (c) 2023-2025 Cory Petkovsek and Contributors
|
||||
# Copyright (c) 2021 J. Cuellar
|
||||
|
||||
class_name ScatterLib
|
||||
|
||||
# Atmospheric Scattering Lib.
|
||||
# References:
|
||||
# - Preetham and Hoffman Paper:
|
||||
# See: https://developer.amd.com/wordpress/media/2012/10/ATI-LightScattering.pdf
|
||||
|
||||
const n: float = 1.0003 # Index of the air refraction
|
||||
const n2: float = 1.00060009 # Index of the air refraction ˆ 2
|
||||
const N: float = 2.545e25 # Molecular Density
|
||||
const pn: float = 0.035 # Depolatization factor for standard air.
|
||||
|
||||
|
||||
static func compute_wavelenghts_lambda(value: Vector3) -> Vector3:
|
||||
return value * 1e-9
|
||||
|
||||
|
||||
static func compute_wavelenghts(lambda: Vector3) -> Vector3:
|
||||
var k = 4.0
|
||||
var ret: Vector3 = lambda
|
||||
ret.x = pow(lambda.x, k)
|
||||
ret.y = pow(lambda.y, k)
|
||||
ret.z = pow(lambda.z, k)
|
||||
return ret
|
||||
|
||||
|
||||
static func compute_beta_ray(wavelenghts: Vector3) -> Vector3:
|
||||
var kr: float = (8.0 * pow(PI, 3.0) * pow(n2 - 1.0, 2.0) * (6.0 + 3.0 * pn))
|
||||
var ret: Vector3 = 3.0 * N * wavelenghts * (6.0 - 7.0 * pn)
|
||||
ret.x = kr / ret.x
|
||||
ret.y = kr / ret.y
|
||||
ret.z = kr / ret.z
|
||||
return ret
|
||||
|
||||
|
||||
static func compute_beta_mie(mie: float, turbidity: float) -> Vector3:
|
||||
var k: float = 434e-6
|
||||
return Vector3.ONE * mie * turbidity * k
|
||||
|
||||
|
||||
static func get_partial_mie_phase(g: float) -> Vector3:
|
||||
var g2: float = g * g
|
||||
var ret: Vector3
|
||||
ret.x = 1.0 - g2
|
||||
ret.y = 1.0 + g2
|
||||
ret.z = 2.0 * g
|
||||
return ret
|
||||
@@ -0,0 +1 @@
|
||||
uid://dc70wk0t64fwe
|
||||
@@ -0,0 +1,549 @@
|
||||
# Copyright (c) 2023-2025 Cory Petkovsek and Contributors
|
||||
# Copyright (c) 2021 J. Cuellar
|
||||
|
||||
## Sky3D is an Atmosphereic Day/Night Cycle for Godot 4.
|
||||
##
|
||||
## This plugin manages time, moving the sun, moon, and stars, and consolidates environmental lighting controls.
|
||||
## To use it, remove any WorldEnvironment node from you scene, then add a new Sky3D node.
|
||||
## Explore and configure the settings in the Sky3D, SunLight, MoonLight, [SkyDome], and [TimeOfDay] nodes.
|
||||
|
||||
@tool
|
||||
class_name Sky3D
|
||||
extends WorldEnvironment
|
||||
|
||||
## Emitted when the environment has changed to a new resource.
|
||||
signal environment_changed
|
||||
|
||||
const SKY_SHADER: String = "res://addons/sky_3d/shaders/SkyMaterial.gdshader"
|
||||
|
||||
@export_custom(PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_READ_ONLY)
|
||||
var version: String = "2.1"
|
||||
|
||||
## The Sun DirectionalLight.
|
||||
var sun: DirectionalLight3D
|
||||
## The Moon DirectionalLight.
|
||||
var moon: DirectionalLight3D
|
||||
## The TimeOfDay node.
|
||||
var tod: TimeOfDay
|
||||
## The SkyDome node.
|
||||
var sky: SkyDome
|
||||
## The Sky shader.
|
||||
var sky_material: ShaderMaterial
|
||||
|
||||
## Enables all rendering and time tracking.
|
||||
@export var sky3d_enabled: bool = true :
|
||||
set(value):
|
||||
sky3d_enabled = value
|
||||
if value:
|
||||
show_sky()
|
||||
resume()
|
||||
else:
|
||||
hide_sky()
|
||||
pause()
|
||||
|
||||
|
||||
#####################
|
||||
## Visibility
|
||||
#####################
|
||||
|
||||
@export_group("Visibility")
|
||||
|
||||
|
||||
## Enables the sky shader. Disable sky, lights, fog for a black sky or call [method hide_sky].
|
||||
@export var sky_enabled: bool = true :
|
||||
set(value):
|
||||
sky_enabled = value
|
||||
if sky and sky_material:
|
||||
sky_material.set_shader_parameter("sky_visible", value)
|
||||
sky.cumulus_visible = clouds_enabled and value
|
||||
sky.cirrus_visible = clouds_enabled and value
|
||||
|
||||
|
||||
## Enables both 2D and cumulus cloud layers.
|
||||
@export var clouds_enabled: bool = true :
|
||||
set(value):
|
||||
clouds_enabled = value
|
||||
if sky:
|
||||
sky.cumulus_visible = value
|
||||
sky.cirrus_visible = value
|
||||
|
||||
|
||||
## Enables the Sun and Moon [DirectionalLight3D]s.
|
||||
@export var lights_enabled: bool = true :
|
||||
set(value):
|
||||
lights_enabled = value
|
||||
if sky:
|
||||
sky.sun_light_enabled = value
|
||||
sky.moon_light_enabled = value
|
||||
|
||||
|
||||
## Enables the screen space fog shader. Sky3D also works with the other two fog methods built into Godot.
|
||||
@export var fog_enabled: bool = true :
|
||||
set(value):
|
||||
fog_enabled = value
|
||||
if sky:
|
||||
sky.fog_visible = value
|
||||
|
||||
|
||||
## Disables rendering of sky, fog, and lights.
|
||||
func hide_sky() -> void:
|
||||
sky_enabled = false
|
||||
lights_enabled = false
|
||||
fog_enabled = false
|
||||
clouds_enabled = false
|
||||
|
||||
|
||||
## Enables rendering of sky, fog, and lights.
|
||||
func show_sky() -> void:
|
||||
sky_enabled = true
|
||||
lights_enabled = true
|
||||
fog_enabled = true
|
||||
clouds_enabled = true
|
||||
|
||||
|
||||
#####################
|
||||
## Time
|
||||
#####################
|
||||
|
||||
@export_group("Time")
|
||||
|
||||
|
||||
## Allows time to progress in the editor. Alias for [member TimeOfDay.editor_time_enabled].
|
||||
@export var editor_time_enabled: bool = true :
|
||||
set(value):
|
||||
if tod:
|
||||
tod.editor_time_enabled = value
|
||||
get:
|
||||
return tod.editor_time_enabled if tod else editor_time_enabled
|
||||
|
||||
|
||||
## Allows time to progress in game. Alias for [member TimeOfDay.game_time_enabled].
|
||||
@export var game_time_enabled: bool = true :
|
||||
set(value):
|
||||
if tod:
|
||||
tod.game_time_enabled = value
|
||||
get:
|
||||
return tod.game_time_enabled if tod else game_time_enabled
|
||||
|
||||
|
||||
## A readable game date string, eg. '2025-01-01'. Alias for [member TimeOfDay.game_date].
|
||||
@export_custom(PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_READ_ONLY)
|
||||
var game_date: String = "" :
|
||||
get:
|
||||
return tod.game_date if tod else game_date
|
||||
|
||||
|
||||
## A readable game time string, e.g. '08:00:00'. Alias for [member TimeOfDay.game_time].
|
||||
@export_custom(PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_READ_ONLY)
|
||||
var game_time: String = "" :
|
||||
get:
|
||||
return tod.game_time if tod else game_time
|
||||
|
||||
|
||||
## The current in-game time in hours from 0.0 to 23.99. Smaller or larger values than the range will wrap.
|
||||
## Alias for [member TimeOfDay.current_time].
|
||||
@export_range(0.0, 23.9998, 0.01) var current_time: float = 8.0 :
|
||||
set(value):
|
||||
if tod:
|
||||
tod.current_time = value
|
||||
get:
|
||||
return tod.current_time if tod else current_time
|
||||
|
||||
|
||||
## The total length of time for a complete day and night cycle in real world minutes. Setting this to
|
||||
## [param 15] means a full in-game day takes 15 real-world minutes. [member game_time_enabled] must be
|
||||
## enabled for this to work. Negative values moves time backwards. The Witcher 3 uses a 96 minute cycle.
|
||||
## Adjust [member update_interval] to match. Shorter days needs more updates. Longer days need less.
|
||||
## Alias for [member TimeOfDay.minutes_per_day].
|
||||
@export_range(-1440, 1440, 0.1) var minutes_per_day: float = 15.0 :
|
||||
set(value):
|
||||
if tod:
|
||||
tod.minutes_per_day = value
|
||||
get:
|
||||
return tod.minutes_per_day if tod else minutes_per_day
|
||||
|
||||
|
||||
## Frequency of sky updates, per second. The smaller the number, the more frequent the updates and
|
||||
## the smoother the animation. Set to [param 0.016] for 60fps, for example.[br][br]
|
||||
## [b]Note:[/b] Setting this value too small may cause unwanted behavior. See [member Timer.wait_time].
|
||||
## Alias for [member TimeOfDay.update_interval].
|
||||
@export_range(0.016, 10.0) var update_interval: float = 0.016 :
|
||||
set(value):
|
||||
if tod:
|
||||
tod.update_interval = value
|
||||
get:
|
||||
return tod.update_interval if tod else update_interval
|
||||
|
||||
|
||||
## Returns true if the sun is above the horizon.
|
||||
func is_day() -> bool:
|
||||
return sky and sky.is_day()
|
||||
|
||||
|
||||
## Returns true if the sun is below the horizon.
|
||||
func is_night() -> bool:
|
||||
return sky and not sky.is_day()
|
||||
|
||||
|
||||
## Pauses time calculation. Alias for [method TimeOfDay.pause].
|
||||
func pause() -> void:
|
||||
if tod:
|
||||
tod.pause()
|
||||
|
||||
|
||||
## Resumes time calculation. Alias for [method TimeOfDay.resume].
|
||||
func resume() -> void:
|
||||
if tod:
|
||||
tod.resume()
|
||||
|
||||
|
||||
var _contrib_tween: Tween
|
||||
|
||||
# Adjusts sky contribution if transitioning to day or night.
|
||||
func _start_sky_contrib_tween(daytime: bool = is_day()) -> void:
|
||||
if not (sky and environment and is_inside_tree()):
|
||||
return
|
||||
|
||||
if _contrib_tween:
|
||||
_contrib_tween.kill()
|
||||
_contrib_tween = get_tree().create_tween()
|
||||
_contrib_tween.set_parallel(true)
|
||||
|
||||
if daytime:
|
||||
_contrib_tween.tween_property(environment, "ambient_light_sky_contribution", sky_contribution, contribution_tween_time)
|
||||
else:
|
||||
var night_contrib: float = minf(night_sky_contribution, sky_contribution) if night_ambient_boost else sky_contribution
|
||||
_contrib_tween.tween_property(environment, "ambient_light_sky_contribution", night_contrib, contribution_tween_time)
|
||||
|
||||
|
||||
#####################
|
||||
## Lighting
|
||||
#####################
|
||||
|
||||
@export_group("Lighting")
|
||||
|
||||
|
||||
## Light intensity scaled before the tonemapper. Softer highlights.
|
||||
## Alias for [member Environment.camera_attributes].
|
||||
## Connect this same resource to your [member Camera3D.attributes].
|
||||
@export_range(0, 16, 0.005) var camera_exposure: float = 1.0 :
|
||||
set(value):
|
||||
if camera_attributes:
|
||||
camera_attributes.exposure_multiplier = value
|
||||
get:
|
||||
return camera_attributes.exposure_multiplier if camera_attributes else camera_exposure
|
||||
|
||||
|
||||
## Light intensity scaled in post processing. Hotter highlights.
|
||||
## Alias for [member Environment.tonemap_exposure].
|
||||
## Connect this same resource to your [member Camera3D.environment].
|
||||
@export_range(0, 16, 0.005) var tonemap_exposure: float = 1.0 :
|
||||
set(value):
|
||||
if environment:
|
||||
environment.tonemap_exposure = value
|
||||
get:
|
||||
return environment.tonemap_exposure if environment else tonemap_exposure
|
||||
|
||||
|
||||
## Light energy coming from the sky shader. Alias for [member SkyDome.exposure].
|
||||
@export_range(0, 16, 0.005) var skydome_energy: float = 1.0 :
|
||||
set(value):
|
||||
if sky:
|
||||
sky.exposure = value
|
||||
get:
|
||||
return sky.exposure if sky else skydome_energy
|
||||
|
||||
|
||||
## Brightness of and light energy coming from the clouds. Alias for [member SkyDome.cumulus_intensity].
|
||||
@export_range(0, 16, 0.005) var cloud_intensity: float = 0.6 :
|
||||
set(value):
|
||||
if sky:
|
||||
sky.cumulus_intensity = value
|
||||
get:
|
||||
return sky.cumulus_intensity if sky else cloud_intensity
|
||||
|
||||
|
||||
## Maximum brightness of the Sun DirectionalLight, visible during the day.
|
||||
## Alias for [member SkyDome.sun_light_energy].
|
||||
@export_range(0, 16, 0.005) var sun_energy: float = 1.0 :
|
||||
set(value):
|
||||
if sky:
|
||||
sky.sun_light_energy = value
|
||||
get:
|
||||
return sky.sun_light_energy if sky else sun_energy
|
||||
|
||||
|
||||
## Opacity of Sun DirectionalLight shadow. Alias for [member DirectionalLight3D.shadow_opacity].
|
||||
@export_range(0, 1, 0.005) var sun_shadow_opacity: float = 1.0 :
|
||||
set(value):
|
||||
if sun:
|
||||
sun.shadow_opacity = value
|
||||
get:
|
||||
return sun.shadow_opacity if sun else sun_shadow_opacity
|
||||
|
||||
|
||||
## Ratio of ambient light to sky light. Works when there are no Reflection Probes or GI.
|
||||
## Sets the target for [member Environment.ambient_light_sky_contribution], which may change at night
|
||||
## depending on [member night_ambient_boost] and [member night_sky_contribution].
|
||||
@export_range(0, 1, 0.005) var sky_contribution: float = 1.0 :
|
||||
set(value):
|
||||
if environment:
|
||||
sky_contribution = value
|
||||
environment.ambient_light_sky_contribution = value
|
||||
_start_sky_contrib_tween()
|
||||
|
||||
|
||||
## Strength of ambient light. Works when there are no Reflection Probes or GI, and
|
||||
## [member sky_contribution] < 1. Alias for [member Environment.ambient_light_energy].
|
||||
@export_range(0, 16, 0.005) var ambient_energy: float = 1.0 :
|
||||
set(value):
|
||||
environment.ambient_light_energy = value
|
||||
_start_sky_contrib_tween()
|
||||
get:
|
||||
return environment.ambient_light_energy if environment else ambient_energy
|
||||
|
||||
|
||||
@export_subgroup("Night")
|
||||
|
||||
|
||||
## Maximum strength of Moon DirectionalLight, visible at night. Alias for [member SkyDome.moon_light_energy].
|
||||
@export_range(0, 16, 0.005) var moon_energy: float = 0.3 :
|
||||
set(value):
|
||||
if sky:
|
||||
sky.moon_light_energy = value
|
||||
get:
|
||||
return sky.moon_light_energy if sky else moon_energy
|
||||
|
||||
|
||||
## Opacity of Moon DirectionalLight shadow. Alias for [member DirectionalLight3D.shadow_opacity].
|
||||
@export_range(0, 1, 0.005) var moon_shadow_opacity: float = 1.0 :
|
||||
set(value):
|
||||
if moon:
|
||||
moon.shadow_opacity = value
|
||||
get:
|
||||
return moon.shadow_opacity if moon else moon_shadow_opacity
|
||||
|
||||
|
||||
## Enables a lower sky_contribution at night, which allows more ambient energy to show.
|
||||
## To use, ensure there are no ReflectionProbes or GI. Set [member ambient_energy] > 0.
|
||||
## Set [member night_sky_contribution] < [member sky_contribution].
|
||||
## Then at night, [member Environment.ambient_light_sky_contribution] will be set lower, which
|
||||
## will show more [member ambient_energy].
|
||||
@export var night_ambient_boost: bool = true :
|
||||
set(value):
|
||||
night_ambient_boost = value
|
||||
_start_sky_contrib_tween()
|
||||
|
||||
|
||||
## Sets [member Environment.ambient_light_sky_contribution] at night if [member night_ambient_boost] is enabled.
|
||||
## See [member night_ambient_boost] and [member sky_contribution].
|
||||
@export_range(0, 1, 0.005) var night_sky_contribution: float = 0.7 :
|
||||
set(value):
|
||||
night_sky_contribution = value
|
||||
if night_ambient_boost:
|
||||
_start_sky_contrib_tween()
|
||||
|
||||
|
||||
## Transition time for changing sky contribution when shifting between day and night.
|
||||
@export_range(0, 30, 0.05) var contribution_tween_time: float = 3.0
|
||||
|
||||
|
||||
@export_subgroup("Auto Exposure")
|
||||
|
||||
|
||||
## Alias for [member CameraAttributes.auto_exposure_enabled].
|
||||
@export var auto_exposure: bool = false :
|
||||
set(value):
|
||||
if camera_attributes:
|
||||
camera_attributes.auto_exposure_enabled = value
|
||||
get:
|
||||
return camera_attributes.auto_exposure_enabled if camera_attributes else auto_exposure
|
||||
|
||||
|
||||
## Alias for [member CameraAttributes.auto_exposure_scale].
|
||||
@export_range(0.01, 16, 0.005) var auto_exposure_scale: float = 0.4 :
|
||||
set(value):
|
||||
if camera_attributes:
|
||||
camera_attributes.auto_exposure_scale = value
|
||||
get:
|
||||
return camera_attributes.auto_exposure_scale if camera_attributes else auto_exposure_scale
|
||||
|
||||
|
||||
## Alias for [member CameraAttributesPractical.auto_exposure_min_sensitivity].
|
||||
@export_range(0, 1600, 0.5) var auto_exposure_min: float = 0.0 :
|
||||
set(value):
|
||||
if camera_attributes:
|
||||
camera_attributes.auto_exposure_min_sensitivity = value
|
||||
if value > auto_exposure_max:
|
||||
auto_exposure_max = value
|
||||
get:
|
||||
return camera_attributes.auto_exposure_min_sensitivity if camera_attributes else auto_exposure_min
|
||||
|
||||
|
||||
## Alias for [member CameraAttributesPractical.auto_exposure_max_sensitivity].
|
||||
@export_range(30, 64000, 0.5) var auto_exposure_max: float = 800.0 :
|
||||
set(value):
|
||||
if camera_attributes:
|
||||
camera_attributes.auto_exposure_max_sensitivity = value
|
||||
if value < auto_exposure_min:
|
||||
auto_exposure_min = value
|
||||
get:
|
||||
return camera_attributes.auto_exposure_max_sensitivity if camera_attributes else auto_exposure_max
|
||||
|
||||
|
||||
## Alias for [member CameraAttributes.auto_exposure_speed].
|
||||
@export_range(0.1, 64, 0.1) var auto_exposure_speed: float = 0.5 :
|
||||
set(value):
|
||||
if camera_attributes:
|
||||
camera_attributes.auto_exposure_speed = value
|
||||
get:
|
||||
return camera_attributes.auto_exposure_speed if camera_attributes else auto_exposure_speed
|
||||
|
||||
|
||||
#####################
|
||||
## Weather
|
||||
#####################
|
||||
|
||||
@export_group("Weather")
|
||||
|
||||
## Sets the wind speed. Alias for [member SkyDome.wind_speed].
|
||||
@export_custom(PROPERTY_HINT_RANGE, "0,120,0.1,or_greater,or_less,suffix:m/s") var wind_speed: float = 1.0 :
|
||||
set(value):
|
||||
if sky:
|
||||
sky.wind_speed = value
|
||||
get:
|
||||
return sky.wind_speed if sky else wind_speed
|
||||
|
||||
## Sets the wind direction. Zero means the wind is coming from the north, 90 from the east,
|
||||
## 180 from the south and 270 (or -90) from the west. Alias for [member SkyDome.wind_direction].
|
||||
@export_custom(PROPERTY_HINT_RANGE, "-180,180,0.1,radians_as_degrees") var wind_direction: float = 0.0 :
|
||||
set(value):
|
||||
if sky:
|
||||
sky.wind_direction = value
|
||||
get:
|
||||
return sky.wind_direction if sky else wind_direction
|
||||
|
||||
|
||||
#####################
|
||||
## Overlays
|
||||
#####################
|
||||
|
||||
@export_group("Overlays")
|
||||
|
||||
|
||||
## Overlays a grid aligned to the horizon and the sky zenith.
|
||||
## Change color in SkyDome. Alias for [member SkyDome.show_azimuthal_grid].
|
||||
@export var show_azimuthal_grid: bool = false :
|
||||
set(value):
|
||||
if sky:
|
||||
sky.show_azimuthal_grid = value
|
||||
get:
|
||||
return sky.show_azimuthal_grid if sky else show_azimuthal_grid
|
||||
|
||||
|
||||
## Overlays a grid aligned to the celestial equator and the north celestial pole (near Polaris).
|
||||
## Change color in SkyDome. Alias for [member SkyDome.show_equatorial_grid].
|
||||
@export var show_equatorial_grid: bool = false :
|
||||
set(value):
|
||||
if sky:
|
||||
sky.show_equatorial_grid = value
|
||||
get:
|
||||
return sky.show_equatorial_grid if sky else show_equatorial_grid
|
||||
|
||||
|
||||
#####################
|
||||
## Setup
|
||||
#####################
|
||||
|
||||
|
||||
func _notification(what: int) -> void:
|
||||
# Must be after _init and before _enter_tree to properly set vars like 'sky' for setters
|
||||
if what in [ NOTIFICATION_SCENE_INSTANTIATED, NOTIFICATION_ENTER_TREE ]:
|
||||
_initialize()
|
||||
|
||||
|
||||
func _initialize() -> void:
|
||||
# Create default environment
|
||||
if environment == null:
|
||||
environment = Environment.new()
|
||||
environment.background_mode = Environment.BG_SKY
|
||||
environment.ambient_light_source = Environment.AMBIENT_SOURCE_SKY
|
||||
environment.ambient_light_sky_contribution = 0.7
|
||||
environment.ambient_light_energy = 1.0
|
||||
environment.reflected_light_source = Environment.REFLECTION_SOURCE_SKY
|
||||
environment.tonemap_mode = Environment.TONE_MAPPER_ACES
|
||||
environment.tonemap_white = 6
|
||||
emit_signal("environment_changed", environment)
|
||||
|
||||
# Setup Sky material & Upgrade old
|
||||
if environment.sky == null or environment.sky.sky_material is PhysicalSkyMaterial:
|
||||
environment.sky = Sky.new()
|
||||
environment.sky.sky_material = ShaderMaterial.new()
|
||||
environment.sky.sky_material.shader = load(SKY_SHADER)
|
||||
|
||||
# Set a reference to the sky material for easy access.
|
||||
sky_material = environment.sky.sky_material
|
||||
|
||||
# Create default camera attributes
|
||||
if camera_attributes == null:
|
||||
camera_attributes = CameraAttributesPractical.new()
|
||||
|
||||
# Assign children nodes
|
||||
|
||||
if has_node("SunLight"):
|
||||
sun = $SunLight
|
||||
elif is_inside_tree():
|
||||
sun = DirectionalLight3D.new()
|
||||
sun.name = "SunLight"
|
||||
add_child(sun, true)
|
||||
sun.owner = get_tree().edited_scene_root
|
||||
sun.shadow_enabled = true
|
||||
|
||||
if has_node("MoonLight"):
|
||||
moon = $MoonLight
|
||||
elif is_inside_tree():
|
||||
moon = DirectionalLight3D.new()
|
||||
moon.name = "MoonLight"
|
||||
add_child(moon, true)
|
||||
moon.owner = get_tree().edited_scene_root
|
||||
moon.shadow_enabled = true
|
||||
|
||||
# DEPRECATED - Remove 2.2
|
||||
if has_node("Skydome"):
|
||||
$Skydome.name = "SkyDome"
|
||||
if has_node("SkyDome"):
|
||||
sky = $SkyDome
|
||||
sky.environment = environment
|
||||
elif is_inside_tree():
|
||||
sky = SkyDome.new()
|
||||
sky.name = "SkyDome"
|
||||
add_child(sky, true)
|
||||
sky.owner = get_tree().edited_scene_root
|
||||
sky.sun_light_path = "../SunLight"
|
||||
sky.moon_light_path = "../MoonLight"
|
||||
sky.environment = environment
|
||||
|
||||
if has_node("TimeOfDay"):
|
||||
tod = $TimeOfDay
|
||||
elif is_inside_tree():
|
||||
tod = TimeOfDay.new()
|
||||
tod.name = "TimeOfDay"
|
||||
add_child(tod, true)
|
||||
tod.owner = get_tree().edited_scene_root
|
||||
tod.dome_path = "../SkyDome"
|
||||
if sky and not sky.day_night_changed.is_connected(_start_sky_contrib_tween):
|
||||
sky.day_night_changed.connect(_start_sky_contrib_tween)
|
||||
|
||||
|
||||
func _enter_tree() -> void:
|
||||
_start_sky_contrib_tween()
|
||||
|
||||
|
||||
func _set(property: StringName, value: Variant) -> bool:
|
||||
match property:
|
||||
"environment":
|
||||
sky.environment = value
|
||||
environment = value
|
||||
emit_signal("environment_changed", environment)
|
||||
return true
|
||||
return false
|
||||
@@ -0,0 +1 @@
|
||||
uid://deo0gxkmk2ahd
|
||||
@@ -0,0 +1 @@
|
||||
uid://b4tkplvh7fq40
|
||||
@@ -0,0 +1,18 @@
|
||||
# Copyright (c) 2023-2025 Cory Petkovsek and Contributors
|
||||
# Copyright (c) 2021 J. Cuellar
|
||||
|
||||
class_name TOD_Math
|
||||
|
||||
|
||||
static func rev(val: float) -> float:
|
||||
return val - int(floor(val / 360.0)) * 360.0
|
||||
|
||||
|
||||
static func spherical_to_cartesian(theta: float, azimuth: float, radius: float = 1.0) -> Vector3:
|
||||
var ret: Vector3
|
||||
var sinTheta: float = sin(theta)
|
||||
ret.x = sinTheta * sin(azimuth)
|
||||
ret.y = cos(theta)
|
||||
ret.z = sinTheta * cos(azimuth)
|
||||
return ret * radius
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
uid://couf46uxtsrhg
|
||||
@@ -0,0 +1,593 @@
|
||||
# Copyright (c) 2023-2025 Cory Petkovsek and Contributors
|
||||
# Copyright (c) 2021 J. Cuellar
|
||||
|
||||
## TimeOfDay is a component of [Sky3D].
|
||||
##
|
||||
## This class tracks the progress of time, as well as the planetary calculations. See [Sky3D].
|
||||
|
||||
@tool
|
||||
class_name TimeOfDay
|
||||
extends Node
|
||||
|
||||
|
||||
signal time_changed(value)
|
||||
signal minute_changed(value)
|
||||
signal hour_changed(value)
|
||||
signal day_changed(value)
|
||||
signal month_changed(value)
|
||||
signal year_changed(value)
|
||||
|
||||
var _previous_minute: int = -1
|
||||
var _previous_hour: int = -1
|
||||
|
||||
const HOURS_PER_DAY: int = 24
|
||||
const RADIANS_PER_HOUR: float = PI / 12.0
|
||||
const HALFPI: float = PI / 2.0
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
_update_celestial_coords()
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
dome_path = dome_path
|
||||
|
||||
_previous_minute = floori(fmod(current_time, 1.0) * 60.0)
|
||||
_previous_hour = floori(current_time)
|
||||
|
||||
_update_timer = Timer.new()
|
||||
_update_timer.name = "Timer"
|
||||
add_child(_update_timer)
|
||||
_update_timer.timeout.connect(_on_timeout)
|
||||
_update_timer.wait_time = update_interval
|
||||
resume()
|
||||
|
||||
|
||||
func _on_timeout() -> void:
|
||||
if system_sync:
|
||||
_update_time_from_os()
|
||||
else:
|
||||
var delta: float = 0.001 * (Time.get_ticks_msec() - _last_update)
|
||||
_progress_time(delta)
|
||||
_update_celestial_coords()
|
||||
_last_update = Time.get_ticks_msec()
|
||||
|
||||
|
||||
## Halts the automatic progression of in-game time until resumed.
|
||||
func pause() -> void:
|
||||
if is_instance_valid(_update_timer):
|
||||
_update_timer.stop()
|
||||
|
||||
|
||||
## Restarts the automatic progression of in-game time.
|
||||
func resume() -> void:
|
||||
if is_instance_valid(_update_timer):
|
||||
# Assume resuming from a pause, so timer only gets one tick
|
||||
_last_update = Time.get_ticks_msec() - update_interval
|
||||
if (Engine.is_editor_hint() and editor_time_enabled) or \
|
||||
(not Engine.is_editor_hint() and game_time_enabled):
|
||||
_update_timer.start()
|
||||
|
||||
|
||||
#####################
|
||||
## General
|
||||
#####################
|
||||
|
||||
var _update_timer: Timer
|
||||
var _last_update: int = 0
|
||||
var _sky_dome: SkyDome
|
||||
|
||||
@export_group("General")
|
||||
|
||||
## Allows time to progress in the editor.
|
||||
@export var editor_time_enabled: bool = true :
|
||||
set(value):
|
||||
editor_time_enabled = value
|
||||
if Engine.is_editor_hint():
|
||||
if editor_time_enabled:
|
||||
resume()
|
||||
else:
|
||||
pause()
|
||||
|
||||
|
||||
## Allows time to progress in game.
|
||||
@export var game_time_enabled: bool = true :
|
||||
set(value):
|
||||
game_time_enabled = value
|
||||
if not Engine.is_editor_hint():
|
||||
if game_time_enabled:
|
||||
resume()
|
||||
else:
|
||||
pause()
|
||||
|
||||
|
||||
## NodePath to SkyDome
|
||||
@export var dome_path: NodePath :
|
||||
set(value):
|
||||
dome_path = value
|
||||
if dome_path:
|
||||
# DEPRECATED - Remove 2.2
|
||||
if dome_path == NodePath("../Skydome"):
|
||||
dome_path = NodePath("../SkyDome")
|
||||
_sky_dome = get_node_or_null(dome_path)
|
||||
_update_celestial_coords()
|
||||
|
||||
|
||||
## The total length of time for a complete day and night cycle in real world minutes. Setting this to
|
||||
## [param 15] means a full in-game day takes 15 real-world minutes. [member game_time_enabled] must be
|
||||
## enabled for this to work. Negative values moves time backwards. The Witcher 3 uses a 96 minute cycle.
|
||||
## Adjust [member update_interval] to match. Shorter days needs more updates. Longer days need less.
|
||||
@export var minutes_per_day: float = 15.0
|
||||
|
||||
|
||||
## Celestial coordinates are updated based upon a timer, which continuously fires based on
|
||||
## this interval: [0.016, 10s]. Set to the lowest, 0.016 (60fps) if your [member minutes_per_day] is short,
|
||||
## such as less than 15 minutes. The Witcher 3 uses a 96 minute day cycle, so 0.1 (10fps) is adequate.
|
||||
@export_range(0.016, 10) var update_interval: float = 0.016 :
|
||||
set(value):
|
||||
update_interval = clamp(value, .016, 10)
|
||||
if is_instance_valid(_update_timer):
|
||||
_update_timer.wait_time = update_interval
|
||||
resume()
|
||||
|
||||
|
||||
#####################
|
||||
## DateTime
|
||||
#####################
|
||||
|
||||
@export_group("Current Time")
|
||||
|
||||
## Syncronize all of Sky3D with your system clock for a realtime sky, time, and date.
|
||||
@export var system_sync: bool = false
|
||||
|
||||
## A readable game date string, eg. '2025-01-01'. Alias for [member TimeOfDay.game_date].
|
||||
@export_custom(PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_READ_ONLY)
|
||||
var game_date: String = "" :
|
||||
get():
|
||||
return "%04d-%02d-%02d" % [ year, month, day ]
|
||||
|
||||
|
||||
## A readable game time string, e.g. '08:00:00'. Alias for [member TimeOfDay.game_time].
|
||||
@export_custom(PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_READ_ONLY)
|
||||
var game_time: String = "" :
|
||||
get():
|
||||
return "%02d:%02d:%02d" % [ floor(current_time), floor(fmod(current_time, 1.0) * 60.0),
|
||||
floor(fmod(current_time * 60.0, 1.0) * 60.0) ]
|
||||
|
||||
|
||||
## The current in-game time in hours, in the range [0.0 .. 23.9998]
|
||||
@export_range(0.,23.9998) var current_time: float = 8.0 :
|
||||
set(value):
|
||||
if current_time != value:
|
||||
current_time = value
|
||||
while current_time > 23.9999:
|
||||
current_time -= 24
|
||||
day += 1
|
||||
while current_time < 0.0000:
|
||||
current_time += 24
|
||||
day -= 1
|
||||
emit_signal("time_changed", current_time)
|
||||
|
||||
var new_hour: int = floori(current_time)
|
||||
var new_minute: int = floori(fmod(current_time, 1.0) * 60.0)
|
||||
|
||||
if new_hour != _previous_hour:
|
||||
_previous_hour = new_hour
|
||||
emit_signal("hour_changed", new_hour)
|
||||
|
||||
if new_minute != _previous_minute:
|
||||
_previous_minute = new_minute
|
||||
emit_signal("minute_changed", new_minute)
|
||||
|
||||
_update_celestial_coords()
|
||||
|
||||
|
||||
## The current day of the month
|
||||
@export_range(0,31) var day: int = 1 :
|
||||
set(value):
|
||||
if day != value:
|
||||
day = value
|
||||
while day > max_days_per_month():
|
||||
day -= max_days_per_month()
|
||||
month += 1
|
||||
while day < 1:
|
||||
month -= 1
|
||||
day += max_days_per_month()
|
||||
emit_signal("day_changed", day)
|
||||
_update_celestial_coords()
|
||||
|
||||
|
||||
## The current month of the year
|
||||
@export_range(0,12) var month: int = 1 :
|
||||
set(value):
|
||||
if month != value:
|
||||
month = value
|
||||
while month > 12:
|
||||
month -= 12
|
||||
year += 1
|
||||
while month < 1:
|
||||
month += 12
|
||||
year -= 1
|
||||
emit_signal("month_changed", month)
|
||||
_update_celestial_coords()
|
||||
|
||||
|
||||
## The current year
|
||||
@export_range(-9999,9999) var year: int = 2025 :
|
||||
set(value):
|
||||
if year != value:
|
||||
year = value
|
||||
emit_signal("year_changed", year)
|
||||
_update_celestial_coords()
|
||||
|
||||
|
||||
## Determines whether the current year has 366 days.
|
||||
func is_leap_year() -> bool:
|
||||
return (year % 4 == 0) && (year % 100 != 0) || (year % 400 == 0)
|
||||
|
||||
|
||||
## Calculates the number of days in the current month, including adjustments for February during leap years.
|
||||
func max_days_per_month() -> int:
|
||||
match month:
|
||||
1, 3, 5, 7, 8, 10, 12:
|
||||
return 31
|
||||
2:
|
||||
return 29 if is_leap_year() else 28
|
||||
return 30
|
||||
|
||||
|
||||
## Sets [member current_time] from parameters.
|
||||
func set_time(hour: int, minute: int, second: int) -> void:
|
||||
current_time = float(hour) + float(minute) / 60.0 + float(second) / 3600.0
|
||||
|
||||
|
||||
## Sets the in-game date and time from a dictionary.
|
||||
func set_from_datetime_dict(datetime_dict: Dictionary) -> void:
|
||||
year = datetime_dict.year
|
||||
month = datetime_dict.month
|
||||
day = datetime_dict.day
|
||||
set_time(datetime_dict.hour, datetime_dict.minute, datetime_dict.second)
|
||||
|
||||
|
||||
## Returns the current date and time as a Godot Time format compatible dictionary.
|
||||
func get_datetime_dict() -> Dictionary:
|
||||
var datetime_dict: Dictionary = {
|
||||
"year": year,
|
||||
"month": month,
|
||||
"day": day,
|
||||
"hour": floor(current_time),
|
||||
"minute": floor(fmod(current_time, 1.0) * 60.0),
|
||||
"second": floor(fmod(current_time * 60.0, 1.0) * 60.0)
|
||||
}
|
||||
return datetime_dict
|
||||
|
||||
|
||||
## Initializes datetime from Unix timestamp, used by users' save/load extentions.
|
||||
func set_from_unix_timestamp(timestamp: int) -> void:
|
||||
set_from_datetime_dict(Time.get_datetime_dict_from_unix_time(timestamp))
|
||||
|
||||
|
||||
## Converts current datetime to Unix timestamp, used by users' save/load extentions.
|
||||
func get_unix_timestamp() -> int:
|
||||
return Time.get_unix_time_from_datetime_dict(get_datetime_dict())
|
||||
|
||||
|
||||
func _progress_time(delta: float) -> void:
|
||||
var hours_per_real_second: float = 0. if is_zero_approx(minutes_per_day) else HOURS_PER_DAY / (minutes_per_day * 60.0)
|
||||
current_time += delta * hours_per_real_second
|
||||
|
||||
|
||||
func _update_time_from_os() -> void:
|
||||
var date_time_os: Dictionary = Time.get_datetime_dict_from_system()
|
||||
set_time(date_time_os.hour, date_time_os.minute, date_time_os.second)
|
||||
day = date_time_os.day
|
||||
month = date_time_os.month
|
||||
year = date_time_os.year
|
||||
|
||||
|
||||
#####################
|
||||
## Planetary
|
||||
#####################
|
||||
|
||||
@export_group("Planetary And Location")
|
||||
|
||||
var _sun_coords: Vector2 = Vector2.ZERO
|
||||
var _moon_coords: Vector2 = Vector2.ZERO
|
||||
var _sun_distance: float
|
||||
var _mean_sun_longitude: float
|
||||
var _sideral_time: float
|
||||
var _local_sideral_time: float
|
||||
var _sun_orbital_elements := OrbitalElements.new()
|
||||
var _moon_orbital_elements := OrbitalElements.new()
|
||||
|
||||
|
||||
enum CelestialMode { SIMPLE, REALISTIC }
|
||||
## Selects between simple trigonometric approximations or full orbital mechanics for computing sun and moon positions.
|
||||
@export var celestials_calculations := CelestialMode.REALISTIC:
|
||||
set(value):
|
||||
celestials_calculations = value
|
||||
_update_celestial_coords()
|
||||
notify_property_list_changed()
|
||||
|
||||
|
||||
## The observer's position on Earth, north or south of the equator (0°=equator, +90°=North Pole, -90°=South Pole).
|
||||
@export_range(-90, 90, 0.00001, "radians_as_degrees") var latitude: float = deg_to_rad(16.) :
|
||||
set(value):
|
||||
latitude = value
|
||||
_update_celestial_coords()
|
||||
|
||||
|
||||
## The observer's position on Earth, east or west of the prime meridian (0°=Greenwich, +180°=International Date Line).
|
||||
@export_range(-180, 180, 0.00001, "radians_as_degrees") var longitude: float = deg_to_rad(108.) :
|
||||
set(value):
|
||||
longitude = value
|
||||
_update_celestial_coords()
|
||||
|
||||
|
||||
## Specifies the time zone offset from UTC in hours to adjust local time calculations.
|
||||
@export_range(-12,14,.25) var utc: float = 7.0 :
|
||||
set(value):
|
||||
utc = value
|
||||
_update_celestial_coords()
|
||||
|
||||
|
||||
## Calculate moon orbital position. Disable for performance if moon lighting not needed.
|
||||
@export var compute_moon_coords: bool = true :
|
||||
set(value):
|
||||
compute_moon_coords = value
|
||||
_update_celestial_coords()
|
||||
notify_property_list_changed()
|
||||
|
||||
|
||||
## Toggles the calculation of sidereal time and star field rotation for realistic deep space alignments.
|
||||
@export var compute_deep_space_coords: bool = true :
|
||||
set(value):
|
||||
compute_deep_space_coords = value
|
||||
_update_celestial_coords()
|
||||
|
||||
|
||||
## Offset the moon azimuth and altitude. Simple celestial calculation mode only.
|
||||
@export var moon_coords_offset: Vector2 = Vector2(0.0, 0.0) :
|
||||
set(value):
|
||||
moon_coords_offset = value
|
||||
_update_celestial_coords()
|
||||
|
||||
|
||||
## Returns the current time at UTC 0
|
||||
func get_current_time_utc0() -> float:
|
||||
return current_time - utc
|
||||
|
||||
|
||||
func _get_time_scale() -> float:
|
||||
# All divisions must be integer divisions, see: https://stjarnhimlen.se/comp/ppcomp.html#3
|
||||
var leap_term: int = int(7 * int((year + int((month + 9) / 12))) / 4)
|
||||
var century_term: int = int(3 * int((int((year + int((month - 9) / 7))) / 100) + 1) / 4)
|
||||
var month_term: int = int(275 * month / 9)
|
||||
var time_scale: int = 367 * year - leap_term - century_term + month_term + day - 730515
|
||||
return time_scale + current_time / 24.0
|
||||
|
||||
|
||||
func _get_oblecl() -> float:
|
||||
# From: https://stjarnhimlen.se/comp/ppcomp.html#4
|
||||
# Another quantity we will need is ecl, the obliquity of the ecliptic,
|
||||
# i.e. the "tilt" of the Earth's axis of rotation (currently 23.4 degrees
|
||||
# and slowly decreasing). First, compute the "d" of the moment of interest
|
||||
# (section 3). Then, compute the obliquity of the ecliptic:
|
||||
return deg_to_rad(23.4393 - 3.563e-7 * _get_time_scale())
|
||||
|
||||
|
||||
func _update_celestial_coords() -> void:
|
||||
if not _sky_dome:
|
||||
return
|
||||
|
||||
match celestials_calculations:
|
||||
CelestialMode.SIMPLE:
|
||||
_compute_simple_sun_coords()
|
||||
_sky_dome.sun_altitude = _sun_coords.y
|
||||
_sky_dome.sun_azimuth = _sun_coords.x
|
||||
if compute_moon_coords:
|
||||
_compute_simple_moon_coords()
|
||||
_sky_dome.moon_altitude = _moon_coords.y
|
||||
_sky_dome.moon_azimuth = _moon_coords.x
|
||||
# Celestial North Pole (SIMPLE convention: +Z = north)
|
||||
_sky_dome.celestial_north_pole = Vector3(0.0, sin(latitude), cos(latitude))
|
||||
|
||||
if compute_deep_space_coords:
|
||||
if _sky_dome.is_scene_built:
|
||||
_sky_dome.sky_material.set_shader_parameter("star_tilt", HALFPI - latitude)
|
||||
|
||||
CelestialMode.REALISTIC:
|
||||
_compute_realistic_sun_coords()
|
||||
_sky_dome.sun_altitude = -_sun_coords.y
|
||||
_sky_dome.sun_azimuth = -_sun_coords.x
|
||||
if compute_moon_coords:
|
||||
_compute_realistic_moon_coords()
|
||||
_sky_dome.moon_altitude = -_moon_coords.y
|
||||
_sky_dome.moon_azimuth = -_moon_coords.x
|
||||
# Celestial North Pole (REALISTIC convention: -Z = north)
|
||||
_sky_dome.celestial_north_pole = Vector3(0.0, sin(latitude), -cos(latitude))
|
||||
|
||||
if compute_deep_space_coords:
|
||||
if _sky_dome.is_scene_built:
|
||||
_sky_dome.sky_material.set_shader_parameter("star_tilt", latitude - HALFPI)
|
||||
_sky_dome.sky_material.set_shader_parameter("star_rotation", -_local_sideral_time)
|
||||
_sky_dome.update_moon_coords()
|
||||
|
||||
|
||||
func _compute_simple_sun_coords() -> void:
|
||||
# PI/12.0 radians = 15 degrees => 1 hour is 15 degrees of rotation
|
||||
var altitude: float = (get_current_time_utc0() * RADIANS_PER_HOUR) + longitude
|
||||
_sun_coords.y = PI - altitude
|
||||
_sun_coords.x = latitude
|
||||
|
||||
|
||||
func _compute_simple_moon_coords() -> void:
|
||||
_moon_coords.y = (PI - _sun_coords.y) + moon_coords_offset.y
|
||||
_moon_coords.x = (PI + _sun_coords.x) + moon_coords_offset.x
|
||||
|
||||
|
||||
func _compute_realistic_sun_coords() -> void:
|
||||
# Orbital Elements
|
||||
_sun_orbital_elements.get_orbital_elements(0, _get_time_scale())
|
||||
_sun_orbital_elements.M = TOD_Math.rev(_sun_orbital_elements.M)
|
||||
|
||||
# Mean anomaly in radians
|
||||
var MRad: float = deg_to_rad(_sun_orbital_elements.M)
|
||||
|
||||
# Eccentric Anomaly
|
||||
var E: float = _sun_orbital_elements.M + rad_to_deg(_sun_orbital_elements.e * sin(MRad) * (1 + _sun_orbital_elements.e * cos(MRad)))
|
||||
|
||||
var ERad: float = deg_to_rad(E)
|
||||
|
||||
# Rectangular coordinates of the sun in the plane of the ecliptic
|
||||
var xv: float = cos(ERad) - _sun_orbital_elements.e
|
||||
var yv: float = sin(ERad) * sqrt(1 - _sun_orbital_elements.e * _sun_orbital_elements.e)
|
||||
|
||||
# Distance and true anomaly
|
||||
# Convert to distance and true anomaly(r = radians, v = degrees)
|
||||
var r: float = sqrt(xv * xv + yv * yv)
|
||||
|
||||
var v: float = atan2(yv, xv)
|
||||
_sun_distance = r
|
||||
|
||||
# True longitude
|
||||
var lonSun: float = v + deg_to_rad(_sun_orbital_elements.w)
|
||||
lonSun = fmod(lonSun + TAU, TAU) # Wrap to [0, 2π)
|
||||
|
||||
## Ecliptic and ecuatorial coords
|
||||
|
||||
# Ecliptic rectangular coords
|
||||
var xs: float = r * cos(lonSun)
|
||||
var ys: float = r * sin(lonSun)
|
||||
|
||||
# Ecliptic rectangular coordinates rotate these to equatorial coordinates
|
||||
var obleclCos: float = cos(_get_oblecl())
|
||||
var obleclSin: float = sin(_get_oblecl())
|
||||
|
||||
var xe: float = xs
|
||||
var ye: float = ys * obleclCos - 0.0 * obleclSin
|
||||
var ze: float = ys * obleclSin + 0.0 * obleclCos
|
||||
|
||||
# Ascencion and declination
|
||||
var RA: float = atan2(ye, xe) # right ascension.
|
||||
var decl: float = atan2(ze, sqrt(xe * xe + ye * ye)) # declination
|
||||
|
||||
# Mean longitude
|
||||
var L: float = deg_to_rad(_sun_orbital_elements.w + _sun_orbital_elements.M)
|
||||
L = fmod(L + TAU, TAU) # Wrap to [0, 2π)
|
||||
_mean_sun_longitude = L
|
||||
var GMST0: float = L + PI
|
||||
|
||||
# Sidereal, see: https://stjarnhimlen.se/comp/ppcomp.html#5b
|
||||
# Local Sidereal Time = GMST0 + UT + longitude
|
||||
_sideral_time = GMST0 + get_current_time_utc0() * RADIANS_PER_HOUR + longitude
|
||||
_local_sideral_time = _sideral_time
|
||||
|
||||
# Hour Angle = Sidereal Time - Right Ascension
|
||||
# See: https://stjarnhimlen.se/comp/ppcomp.html#12b
|
||||
var HA: float = _sideral_time - RA
|
||||
|
||||
# Hour angle and declination in rectangular coords
|
||||
var declCos: float = cos(decl)
|
||||
var x: float = cos(HA) * declCos # X Axis points to the celestial equator in the south
|
||||
var y: float = sin(HA) * declCos # Y axis points to the horizon in the west
|
||||
var z: float = sin(decl) # Z axis points to the north celestial pole
|
||||
|
||||
# Rotate the rectangualar coordinates system along of the Y axis
|
||||
var sinLat: float = sin(latitude)
|
||||
var cosLat: float = cos(latitude)
|
||||
var xhor: float = x * sinLat - z * cosLat
|
||||
var yhor: float = y
|
||||
var zhor: float = x * cosLat + z * sinLat
|
||||
|
||||
# Azimuth and altitude
|
||||
_sun_coords.x = atan2(yhor, xhor) + PI
|
||||
_sun_coords.y = (PI * 0.5) - asin(zhor)
|
||||
|
||||
|
||||
func _compute_realistic_moon_coords() -> void:
|
||||
# Orbital Elements
|
||||
_moon_orbital_elements.get_orbital_elements(1, _get_time_scale())
|
||||
_moon_orbital_elements.N = TOD_Math.rev(_moon_orbital_elements.N)
|
||||
_moon_orbital_elements.w = TOD_Math.rev(_moon_orbital_elements.w)
|
||||
_moon_orbital_elements.M = TOD_Math.rev(_moon_orbital_elements.M)
|
||||
|
||||
var NRad: float = deg_to_rad(_moon_orbital_elements.N)
|
||||
var IRad: float = deg_to_rad(_moon_orbital_elements.i)
|
||||
var MRad: float = deg_to_rad(_moon_orbital_elements.M)
|
||||
|
||||
# Eccentric anomaly
|
||||
var E: float = _moon_orbital_elements.M + rad_to_deg(_moon_orbital_elements.e * sin(MRad) * (1 + _moon_orbital_elements.e * cos(MRad)))
|
||||
var ERad: float = deg_to_rad(E)
|
||||
|
||||
# See: https://stjarnhimlen.se/comp/ppcomp.html#6
|
||||
var xv: float = _moon_orbital_elements.a * (cos(ERad) - _moon_orbital_elements.e)
|
||||
var yv: float = _moon_orbital_elements.a * sqrt(1 - _moon_orbital_elements.e * _moon_orbital_elements.e) * sin(ERad)
|
||||
|
||||
# Convert to distance and true anomaly(r = radians, v = degrees)
|
||||
var r: float = sqrt(xv * xv + yv * yv)
|
||||
var v: float = atan2(yv, xv)
|
||||
var l: float = v + deg_to_rad(_moon_orbital_elements.w)
|
||||
|
||||
var cosL: float = cos(l)
|
||||
var sinL: float = sin(l)
|
||||
var cosNRad: float = cos(NRad)
|
||||
var sinNRad: float = sin(NRad)
|
||||
var cosIRad: float = cos(IRad)
|
||||
|
||||
var xeclip: float = r * (cosNRad * cosL - sinNRad * sinL * cosIRad)
|
||||
var yeclip: float = r * (sinNRad * cosL + cosNRad * sinL * cosIRad)
|
||||
var zeclip: float = r * (sinL * sin(IRad))
|
||||
|
||||
# Position in space: https://stjarnhimlen.se/comp/ppcomp.html#7
|
||||
# Geocentric position for the moon and Heliocentric position for the planets
|
||||
var lonecl: float = atan2(yeclip, xeclip) # Ecliptic longitude
|
||||
var latecl: float = atan2(zeclip, sqrt(xeclip * xeclip + yeclip * yeclip)) # Ecliptic latitude
|
||||
|
||||
var nr: float = 1.0
|
||||
var xh: float = nr * cos(lonecl) * cos(latecl)
|
||||
var yh: float = nr * sin(lonecl) * cos(latecl)
|
||||
var zh: float = nr * sin(latecl)
|
||||
|
||||
# Geocentric coords
|
||||
var xs: float = 0.0
|
||||
var ys: float = 0.0
|
||||
|
||||
# Convert the geocentric position to heliocentric position
|
||||
var xg: float = xh + xs
|
||||
var yg: float = yh + ys
|
||||
var zg: float = zh
|
||||
|
||||
# Ecuatorial coords
|
||||
# Convert xg, yg to equatorial coords
|
||||
var obleclCos: float = cos(_get_oblecl())
|
||||
var obleclSin: float = sin(_get_oblecl())
|
||||
|
||||
var xe: float = xg
|
||||
var ye: float = yg * obleclCos - zg * obleclSin
|
||||
var ze: float = yg * obleclSin + zg * obleclCos
|
||||
|
||||
# Right ascention
|
||||
var RA: float = atan2(ye, xe)
|
||||
var decl: float = atan2(ze, sqrt(xe * xe + ye * ye))
|
||||
|
||||
# Sideral time and hour angle
|
||||
var HA: float = ((_sideral_time) - RA)
|
||||
|
||||
# HA y Decl in rectangular coordinates
|
||||
var declCos: float = cos(decl)
|
||||
var xr: float = cos(HA) * declCos
|
||||
var yr: float = sin(HA) * declCos
|
||||
var zr: float = sin(decl)
|
||||
|
||||
# Rotate the rectangualar coordinates system along of the Y axis(radians)
|
||||
var sinLat: float = sin(latitude)
|
||||
var cosLat: float = cos(latitude)
|
||||
|
||||
var xhor: float = xr * sinLat - zr * cosLat
|
||||
var yhor: float = yr
|
||||
var zhor: float = xr * cosLat + zr * sinLat
|
||||
|
||||
# Azimuth and altitude
|
||||
_moon_coords.x = atan2(yhor, xhor) + PI
|
||||
_moon_coords.y = (PI *0.5) - atan2(zhor, sqrt(xhor * xhor + yhor * yhor)) # Mathf.Asin(zhor)
|
||||
@@ -0,0 +1 @@
|
||||
uid://cjmkcj8hwm7ts
|
||||
@@ -1,133 +0,0 @@
|
||||
@tool
|
||||
class_name CarryArms
|
||||
extends Node
|
||||
## 抱东西时把两条胳膊伸出去抱住物品,走路时人和东西一起晃。
|
||||
##
|
||||
## 模型里没有「抱」这个动作片段,所以这里在动画**之后**叠加一段手臂姿态:
|
||||
## 只动 arm_l_* / arm_r_* 这几根骨骼,其余骨骼照旧交给 AnimationPlayer。
|
||||
## process_priority 设得比播放器大,保证在它写完骨骼之后才覆盖 —— 否则会被动画冲掉。
|
||||
##
|
||||
## 晃动同时作用在两条胳膊和「抱持点」上:物品每帧跟住抱持点,于是东西和人一起晃,
|
||||
## 不会出现「胳膊在晃、东西钉在手上」的穿帮。
|
||||
##
|
||||
## 控制器只负责说「抱没抱、走多快」,具体摆成什么样全由这里的参数决定。
|
||||
##
|
||||
## 标了 @tool 是为了能在编辑器里跑测试(非 @tool 脚本在编辑器里是 placeholder,方法调不动)。
|
||||
|
||||
@export_group("接线")
|
||||
## 骨架。本节点挂在 Body 下,所以要先 .. 回到 Body
|
||||
@export var skeleton_path: NodePath = ^"../mouse/MouseRig/Skeleton3D"
|
||||
## 抱持点(Marker3D)。物品就摆在它上面:Body/CarryArms → ../../CameraRig/CarrySocket
|
||||
@export var socket_path: NodePath = ^"../../CameraRig/CarrySocket"
|
||||
|
||||
@export_group("抱持姿势")
|
||||
## 大臂前摆(度):把手伸到身前
|
||||
@export var shoulder_swing_deg: float = 40.0
|
||||
## 大臂内收(度):两只手往中间合,抱住东西
|
||||
@export var shoulder_tuck_deg: float = 50.0
|
||||
## 大臂侧收(度):绕身体前后轴转,把胳膊从上往下收到身前
|
||||
@export var shoulder_roll_deg: float = 0.0
|
||||
## 小臂弯曲(度)
|
||||
@export var elbow_deg: float = 15.0
|
||||
## 胳膊沿自身方向拉长的倍数(>1 就是「延伸」)
|
||||
@export var stretch: float = 1.25
|
||||
|
||||
@export_group("走路晃动")
|
||||
## 胳膊晃动幅度(度)
|
||||
@export var wobble_deg: float = 8.0
|
||||
## 抱持点晃动幅度(米)。物品跟着它晃,所以别太大
|
||||
@export var wobble_socket: float = 0.035
|
||||
## 每秒晃几下
|
||||
@export var wobble_hz: float = 1.7
|
||||
## 姿态过渡速度(越大越跟手)
|
||||
@export var blend_speed: float = 8.0
|
||||
|
||||
var _skeleton: Skeleton3D
|
||||
var _socket: Node3D
|
||||
var _socket_rest := Transform3D.IDENTITY
|
||||
## 手臂骨骼索引,按 [左大臂, 左小臂, 左手, 右大臂, 右小臂, 右手] 的顺序
|
||||
var _bones: Array[int] = []
|
||||
var _carrying := false
|
||||
## 姿态权重 0~1:抱起来时涨到 1,放下时退回 0(退回 0 之后就完全交给动画)
|
||||
var _weight := 0.0
|
||||
var _phase := 0.0
|
||||
## 当前速度 / 常规走速,用来决定晃多厉害
|
||||
var _speed_ratio := 0.0
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
# 跑在 AnimationPlayer 之后,否则我们摆的骨骼会被动画覆盖
|
||||
process_priority = 100
|
||||
_skeleton = get_node_or_null(skeleton_path) as Skeleton3D
|
||||
if _skeleton == null:
|
||||
push_warning("%s 找不到骨架(%s),胳膊不会动" % [get_path(), skeleton_path])
|
||||
_socket = get_node_or_null(socket_path) as Node3D
|
||||
if _socket != null:
|
||||
_socket_rest = _socket.transform
|
||||
for bone_name in ["arm_l_01", "arm_l_02", "arm_l_03", "arm_r_01", "arm_r_02", "arm_r_03"]:
|
||||
if _skeleton == null:
|
||||
break
|
||||
var index := _skeleton.find_bone(bone_name)
|
||||
if index < 0:
|
||||
push_warning("%s 里没有骨骼 %s,胳膊姿势会不完整" % [_skeleton.get_path(), bone_name])
|
||||
_bones.append(index)
|
||||
|
||||
|
||||
## 控制器每帧告诉它「抱没抱、走多快」。speed_ratio 用「当前水平速度 / 常规走速」。
|
||||
func set_carrying(carrying: bool, speed_ratio: float) -> void:
|
||||
_carrying = carrying
|
||||
_speed_ratio = speed_ratio
|
||||
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
var target := 1.0 if _carrying else 0.0
|
||||
_weight = move_toward(_weight, target, blend_speed * delta)
|
||||
if _weight <= 0.0:
|
||||
# 没抱东西:不碰骨骼也不碰抱持点,全部交回给动画
|
||||
return
|
||||
|
||||
# 走得越快晃得越猛;站着不动时 phase 不推进,姿态是静的
|
||||
var wobble_scale := clampf(_speed_ratio, 0.0, 1.5)
|
||||
_phase += delta * TAU * wobble_hz * wobble_scale
|
||||
var wobble := sin(_phase) * wobble_deg * wobble_scale * _weight
|
||||
|
||||
if _socket != null:
|
||||
# 抱持点跟着晃,物品每帧跟住它 —— 东西和胳膊就同步了
|
||||
var bob := sin(_phase) * wobble_socket * wobble_scale * _weight
|
||||
_socket.transform = _socket_rest.translated_local(Vector3(0.0, bob, 0.0))
|
||||
|
||||
if _skeleton == null or _bones.size() < 6:
|
||||
return
|
||||
# 左右两组,同相位(抱着重物是两只手一起上下),内收方向相反
|
||||
for side in 2:
|
||||
var shoulder := _bones[side * 3]
|
||||
var elbow := _bones[side * 3 + 1]
|
||||
var wrist := _bones[side * 3 + 2]
|
||||
if shoulder < 0:
|
||||
continue
|
||||
var mirror := -1.0 if side == 0 else 1.0
|
||||
# 参数是**世界方向**:X 负向 = 往前摆,Y = 左右收,Z = 侧收(左右手相反)
|
||||
_apply_bone(shoulder, Vector3(
|
||||
-(shoulder_swing_deg + wobble),
|
||||
shoulder_tuck_deg * mirror,
|
||||
shoulder_roll_deg * mirror,
|
||||
))
|
||||
# 胳膊沿自身拉长 —— 这就是「可延伸」
|
||||
_skeleton.set_bone_pose_scale(shoulder, Vector3(1.0, lerpf(1.0, stretch, _weight), 1.0))
|
||||
if elbow >= 0:
|
||||
_apply_bone(elbow, Vector3(-elbow_deg, 0.0, 0.0))
|
||||
if wrist >= 0:
|
||||
_apply_bone(wrist, Vector3(-elbow_deg * 0.4, 0.0, 0.0))
|
||||
|
||||
|
||||
## 在动画写好的姿态上再叠一个旋转。
|
||||
##
|
||||
## 传进来的角度按**世界方向**理解(往前摆 = 绕 X 轴负向),内部用骨骼自身的静止姿态
|
||||
## 把它换算到骨骼本地空间 —— 否则得先猜出每根骨骼本地轴朝哪,换个模型就全错。
|
||||
## 叠在动画姿态上是乘法,所以走路原本的摆臂不会被抹掉。
|
||||
func _apply_bone(index: int, offset_deg: Vector3) -> void:
|
||||
var rest_basis := _skeleton.get_bone_rest(index).basis
|
||||
var world := Basis.from_euler(offset_deg * PI / 180.0)
|
||||
var local := rest_basis.inverse() * world * rest_basis
|
||||
var posed := _skeleton.get_bone_pose_rotation(index)
|
||||
_skeleton.set_bone_pose_rotation(index, posed * local.get_rotation_quaternion())
|
||||
@@ -1 +0,0 @@
|
||||
uid://cca1hl0g2bku
|
||||
@@ -1,51 +1,12 @@
|
||||
extends CharacterBody3D
|
||||
## 第一人称控制器:只管「怎么动」—— 输入、移动物理、视角、血条、联机分叉。
|
||||
## 表现层(动画)在 Body/AnimationPlayer 上(挂 player_animator.gd),本文件不认识任何片段名。
|
||||
##
|
||||
## 场景结构:
|
||||
## Player (CharacterBody3D) ← 只负责移动与碰撞,自身不旋转
|
||||
## ├─ Collision (CollisionShape3D, CapsuleShape3D)
|
||||
## ├─ CameraRig (Node3D) ← 眼睛,架在 eye_height 高,承担偏航 yaw
|
||||
## │ ├─ Camera (Camera3D) ← 承担俯仰 pitch(第一人称,没有弹簧臂)
|
||||
## │ └─ CarrySocket (Marker3D) ← 抱持点:抱起来的物品就摆在这上面
|
||||
## ├─ Body (Node3D) ← 视觉层,始终朝视线方向;换角色模型时挂在它下面
|
||||
## │ ├─ mouse (Node3D) ← 模型(mouse.glb):MouseRig/Skeleton3D + 各部位 MeshInstance3D
|
||||
## │ ├─ AnimationPlayer ← 动作库 + 表现层(挂 player_animator.gd):只读运动、只写动画
|
||||
## │ └─ CarryArms (Node) ← 抱东西时的胳膊姿势 + 走路晃动(挂 carry_arms.gd)
|
||||
## ├─ Sync (MultiplayerSynchronizer) ← 同步 position、Body.rotation、health、动画状态
|
||||
## ├─ HealthLabel (Label3D) ← 头顶血条(节点配置,不是代码建的)
|
||||
## └─ InteractHint (Label3D) ← 交互提示,同样在场景里配好
|
||||
##
|
||||
## 滑索:走到缆下按 E 挂上,沿缆滑向低端,缆上按 S 刹车、空格跳离。
|
||||
## 手感参数(速度/刹车/吸附半径)全在 Zipline 组件上,本文件只读不定义 —— 见文件末尾。
|
||||
##
|
||||
## 抱起:左键抱起最近的可抱物品,长按 Q 蓄力、松手扔出去。
|
||||
## 谁能被抱由物品自己决定(挂 Carryable 脚本、进 carryable 组),手感参数同样在物品上;
|
||||
## 本文件只管「找到它、摆到抱持点、扔出去」这三件事。
|
||||
##
|
||||
## 操作:WASD/方向键移动,鼠标转视角,Shift 疾跑,Ctrl 蹲下(蹲着走即潜行),空格跳跃,
|
||||
## E 滑索,左键抱起,长按 Q 扔出,Esc 释放/锁定鼠标
|
||||
|
||||
## 跳跃输入缓冲帧数(输入事件在渲染帧到达,物理帧读取,避免漏跳)
|
||||
const JUMP_BUFFER_FRAMES: int = 6
|
||||
|
||||
@export_group("属性")
|
||||
## 血量
|
||||
@export var health: int = 100:
|
||||
set(value):
|
||||
health = clampi(value, 0, 100)
|
||||
_refresh_health_label()
|
||||
## 跳跃输入缓冲(秒):按下跳跃后这段时间内若落地,自动补上一次起跳
|
||||
const JUMP_BUFFER_TIME: float = 0.1
|
||||
|
||||
@export_group("视角")
|
||||
## 眼睛高度(米)。相机就架在这个高度上 —— 按碰撞胶囊定,跟模型高矮无关。
|
||||
@export var eye_height: float = 1.6
|
||||
## 第一人称下藏掉自己的身体。**两条胳膊会留下来** —— 抱东西时得看得见自己的手。
|
||||
## 想连胳膊也看不见就把这个关掉(那样相机要么在模型里、要么贴着头顶,画面会糊一片)。
|
||||
## 只影响本机这一份 —— 远端玩家的模型照常显示。
|
||||
@export var hide_own_body: bool = true
|
||||
## 鼠标灵敏度(弧度/像素)
|
||||
@export var mouse_sensitivity: float = 0.0025
|
||||
## 最低俯角(向下看)。第一人称得能抬头低头,所以放到接近 ±90°
|
||||
## 最低俯角(向下看)
|
||||
@export var pitch_min_deg: float = -89.0
|
||||
## 最高仰角(向上看)
|
||||
@export var pitch_max_deg: float = 89.0
|
||||
@@ -62,108 +23,40 @@ const JUMP_BUFFER_FRAMES: int = 6
|
||||
## 起跳初速度(米/秒)
|
||||
@export var jump_velocity: float = 5.0
|
||||
|
||||
@export_group("抱起")
|
||||
## 多远内的物品能抱起来(米)
|
||||
@export var pickup_radius: float = 3.0
|
||||
## 抱着东西时的移动速度倍率
|
||||
@export var carry_speed_scale: float = 0.6
|
||||
## 抱着东西时禁止疾跑
|
||||
@export var carry_blocks_sprint: bool = true
|
||||
## 长按 Q 蓄满力要多久(秒)
|
||||
@export var throw_charge_time: float = 0.8
|
||||
## 扔出去时额外叠上玩家自身的速度(跑着扔会飞更远)
|
||||
@export var throw_inherits_velocity: bool = true
|
||||
## 等服务器确认抱起的宽限时间(秒)。联机下被别人先抢走时,超时就自动松手
|
||||
@export var pickup_confirm_time: float = 0.6
|
||||
|
||||
@onready var body: Node3D = $Body
|
||||
## 眼睛分两层转:CameraRig 绕竖直轴转(yaw),Camera 原地抬头低头(pitch)。
|
||||
## 分开是为了让 pitch 不掺进移动方向 —— 移动只用 rig 的 basis,低头看地也不会走歪。
|
||||
@onready var camera_rig: Node3D = $CameraRig
|
||||
@onready var camera: Camera3D = $CameraRig/Camera
|
||||
## 头顶血条是 player.tscn 里的 Label3D 节点(3D 世界里的文字,会跟着角色走)
|
||||
@onready var _health_label: Label3D = $HealthLabel
|
||||
## 滑索操作提示,同样在 player.tscn 里配好,默认隐藏
|
||||
@onready var _hint: Label3D = $InteractHint
|
||||
## 抱东西时的胳膊表现层(挂 carry_arms.gd):只收「抱没抱、走多快」。
|
||||
## 抱持点(CameraRig/CarrySocket)不在这里引用 —— 物品自己按名字找持有者的抱持点,
|
||||
## 这样每端算出来的位置都一样,联机不用同步物品位置。
|
||||
@onready var _carry_arms: CarryArms = $Body/CarryArms
|
||||
|
||||
## 动画状态机(挂在 Body/AnimationPlayer 上)。只把「运动结果」交给它,也只知道它会自己播动画。
|
||||
## 动画状态机。控制器只把「运动结果」交给它,它也只管自己播动画。
|
||||
@onready var animator: PlayerAnimator = $Body/AnimationPlayer
|
||||
@onready var interaction_pos: Marker3D = $CameraRig/Camera/InteractionPos
|
||||
@onready var interaction_ray: RayCast3D = $CameraRig/Camera/InteractionRay
|
||||
|
||||
var _gravity: float = float(ProjectSettings.get_setting("physics/3d/default_gravity", 9.8))
|
||||
var _yaw: float = 0.0
|
||||
var _pitch: float = 0.0
|
||||
var _jump_buffer: int = 0
|
||||
## 本节点是否由本机玩家操控。由世界节点在生成时设置 authority 决定,见 world/main.gd。
|
||||
var _is_local: bool = true
|
||||
|
||||
## 当前挂着的滑索。非 null 时 _physics_process 走缆上分支,常规移动/重力/贴地全部让位。
|
||||
## 只在本机权威端写;远端玩家的位置由 Sync 复制过来,跟着缆一起动。
|
||||
var _zipline: Zipline = null
|
||||
## 沿缆的进度参数 t∈[0,1]。缆的几何由 Zipline 负责,这里只记「我走到哪了」。
|
||||
var _zip_t: float = 0.0
|
||||
## 滑动方向:+1 往 AnchorB、-1 往 AnchorA。永远是往低的那一端
|
||||
var _zip_dir: int = 1
|
||||
## 沿缆速度(米/秒)
|
||||
var _zip_speed: float = 0.0
|
||||
## 挂上瞬间「人相对缆」的偏移,随后衰减到 0 —— 吸附是滑过去的,不是瞬移
|
||||
var _zip_grab_offset: Vector3 = Vector3.ZERO
|
||||
|
||||
## 手上抱着的物品。位置由物品自己跟住抱持点(见 Carryable),玩家不插手 ——
|
||||
## 这样每端算出来的位置都一样,联机不用同步物品位置。
|
||||
var _carried: Carryable = null
|
||||
## 长按 Q 的蓄力进度 0~1
|
||||
var _throw_charge: float = 0.0
|
||||
var _throw_charging: bool = false
|
||||
## 等服务器确认抱起的剩余时间。服务器没给(被别人先抢了)就自己松手
|
||||
var _pickup_wait: float = 0.0
|
||||
var _jump_buffer: float = 0.0
|
||||
# 拾取
|
||||
var _pickObj = null
|
||||
var _pickPower = 10
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
_report_identity()
|
||||
# 动画准备不在这里 —— PlayerAnimator 有自己的 _ready(),而子节点先于本节点 ready
|
||||
_is_local = is_multiplayer_authority()
|
||||
if not _is_local:
|
||||
# 远端玩家只是一具「回放身体」:
|
||||
# 拆掉相机 —— 否则每多一个玩家就多一台 Camera3D 互相抢渲染;
|
||||
# 关掉输入与物理 —— 否则与权威端双重模拟,表现为抖动、漂移、穿地。
|
||||
# 它的 position 与 Body.rotation 由子节点 Sync 同步过来;
|
||||
# 头顶血条保留 —— health 同样是同步过来的,这样谁都能看到别人还剩多少血。
|
||||
# 远端玩家只是一具「回放身体」:
|
||||
# 拆掉相机 —— 否则每多一个玩家就多一台 Camera3D 抢渲染;
|
||||
# 关掉输入与物理 —— 否则与权威端双重模拟,表现为抖动、漂移、穿地。
|
||||
# 它的 position 与 Body.rotation 由 Sync 子节点同步;血条由 health 同步。
|
||||
if not is_multiplayer_authority():
|
||||
camera_rig.queue_free()
|
||||
set_physics_process(false)
|
||||
set_process_unhandled_input(false)
|
||||
return
|
||||
camera_rig.position.y = eye_height
|
||||
if hide_own_body:
|
||||
_hide_body_keep_arms()
|
||||
|
||||
_yaw = camera_rig.rotation.y
|
||||
_pitch = clampf(camera.rotation.x, deg_to_rad(pitch_min_deg), deg_to_rad(pitch_max_deg))
|
||||
_apply_look()
|
||||
set_mouse_captured(true)
|
||||
# 同步可能在 _ready() 之前就把 health 写进来了,那时血条还没刷新,这里补一次
|
||||
_refresh_health_label()
|
||||
|
||||
|
||||
## 第一人称:藏掉身体,但**把两条胳膊留下** —— 抱东西时得看得见自己的手。
|
||||
## 模型各部位是独立 mesh,按名字挑出来即可;名字对不上(换模型了)就退化成整个藏掉。
|
||||
func _hide_body_keep_arms() -> void:
|
||||
var stack: Array[Node] = [body]
|
||||
var kept := 0
|
||||
while not stack.is_empty():
|
||||
var node: Node = stack.pop_back()
|
||||
for child in node.get_children():
|
||||
stack.append(child)
|
||||
var mesh := child as MeshInstance3D
|
||||
if mesh == null:
|
||||
continue
|
||||
var is_arm := str(mesh.name).begins_with("arm_")
|
||||
mesh.visible = is_arm
|
||||
kept += 1 if is_arm else 0
|
||||
if kept == 0:
|
||||
body.visible = false
|
||||
|
||||
|
||||
func _unhandled_input(event: InputEvent) -> void:
|
||||
@@ -176,40 +69,23 @@ func _unhandled_input(event: InputEvent) -> void:
|
||||
)
|
||||
_apply_look()
|
||||
elif event.is_action_pressed("jump"):
|
||||
# 缆上按跳 = 主动脱手,额外给一个向上的小跳,方便够到旁边的平台
|
||||
if _zipline != null:
|
||||
_detach_zipline(true)
|
||||
else:
|
||||
_jump_buffer = JUMP_BUFFER_FRAMES
|
||||
elif event.is_action_pressed("interact"):
|
||||
if _zipline != null:
|
||||
_detach_zipline()
|
||||
else:
|
||||
_attach_zipline()
|
||||
elif event.is_action_pressed("carry") and Input.mouse_mode == Input.MOUSE_MODE_CAPTURED:
|
||||
# 鼠标没锁定时左键是用来重新锁定视角的,那种情况留给下面那个分支
|
||||
_pick_up()
|
||||
elif event.is_action_pressed("throw_carry"):
|
||||
# 按住开始蓄力,松手才真正出手(见 _release_throw)
|
||||
_throw_charging = _carried != null
|
||||
_throw_charge = 0.0
|
||||
elif event.is_action_released("throw_carry"):
|
||||
_release_throw()
|
||||
_jump_buffer = JUMP_BUFFER_TIME
|
||||
elif event.is_action_pressed("ui_cancel"):
|
||||
set_mouse_captured(Input.mouse_mode != Input.MOUSE_MODE_CAPTURED)
|
||||
elif event is InputEventMouseButton and event.pressed and Input.mouse_mode != Input.MOUSE_MODE_CAPTURED:
|
||||
set_mouse_captured(true)
|
||||
elif event is InputEventKey and event.pressed and not event.echo and event.keycode == KEY_H:
|
||||
# 只有权威端能改被同步的属性,别人改的会被覆盖回去
|
||||
if is_multiplayer_authority():
|
||||
health -= 10
|
||||
|
||||
|
||||
if event is InputEventMouseButton and event.is_action_pressed("carry"):
|
||||
if _pickObj == null:
|
||||
_pickObject()
|
||||
else:
|
||||
_removePickObject()
|
||||
|
||||
func set_mouse_captured(captured: bool) -> void:
|
||||
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED if captured else Input.MOUSE_MODE_VISIBLE
|
||||
|
||||
|
||||
## 把 _yaw / _pitch 落到节点上。鼠标一动就调一次(渲染帧),所以视角是跟手的、不等物理帧。
|
||||
## 把 _yaw / _pitch 落到节点上。鼠标一动就调一次(渲染帧),所以视角跟手、不等物理帧。
|
||||
## 身体跟着一起转 —— 第一人称里「你看哪,身体就朝哪」,别人看你也是这个朝向。
|
||||
func _apply_look() -> void:
|
||||
camera_rig.rotation.y = _yaw
|
||||
@@ -218,50 +94,6 @@ func _apply_look() -> void:
|
||||
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
if not _is_local:
|
||||
return
|
||||
|
||||
# 交互提示:**同一处判定**既决定「显示什么」又决定「按键干什么」,
|
||||
# 免得出现「提示亮了但按 E 挂不上」这种对不上的情况。
|
||||
# 优先级:手上有东西 > 可抱物 > 缆上 > 可挂的缆。
|
||||
var nearby_carry := _find_carryable()
|
||||
var nearby_zip := _find_zipline()
|
||||
if _carried != null:
|
||||
_hint.visible = true
|
||||
_hint.text = "长按 Q 扔出 %d%%" % int(_throw_charge * 100.0)
|
||||
elif nearby_carry != null:
|
||||
_hint.visible = true
|
||||
_hint.text = "左键 抱起"
|
||||
elif _zipline != null:
|
||||
_hint.visible = true
|
||||
_hint.text = "空格 跳离 · E 松手"
|
||||
elif nearby_zip != null:
|
||||
_hint.visible = true
|
||||
_hint.text = "按 E 挂上滑索"
|
||||
else:
|
||||
_hint.visible = false
|
||||
|
||||
# 抱着东西:推进 Q 的蓄力。物品位置不归玩家管 —— 它自己跟住抱持点(见 Carryable),
|
||||
# 每端都这么算,所以联机不用同步物品位置。
|
||||
if _carried != null:
|
||||
if _carried.carrier_peer != multiplayer.get_unique_id():
|
||||
# 服务器还没确认,或者被别人先抢走了:给一小段时间,超时就松手
|
||||
_pickup_wait -= delta
|
||||
if _pickup_wait <= 0.0:
|
||||
_carried = null
|
||||
elif _throw_charging:
|
||||
_throw_charge = minf(1.0, _throw_charge + delta / maxf(throw_charge_time, 0.01))
|
||||
# 胳膊表现层只收「抱没抱、走多快」,摆成什么样是它自己的事
|
||||
_carry_arms.set_carrying(
|
||||
_carried != null,
|
||||
Vector2(velocity.x, velocity.z).length() / maxf(walk_speed, 0.01),
|
||||
)
|
||||
|
||||
# 挂在缆上时,下面的常规重力/输入/贴地全部让位给缆上运动
|
||||
if _zipline != null:
|
||||
_ride_zipline(delta)
|
||||
return
|
||||
|
||||
# --- 重力 ---
|
||||
if is_on_floor():
|
||||
if velocity.y < 0.0:
|
||||
@@ -270,37 +102,28 @@ func _physics_process(delta: float) -> void:
|
||||
velocity.y -= _gravity * delta
|
||||
|
||||
# --- 跳跃(读取输入缓冲)---
|
||||
if _jump_buffer > 0:
|
||||
_jump_buffer -= 1
|
||||
if _jump_buffer > 0.0:
|
||||
_jump_buffer -= delta
|
||||
if is_on_floor():
|
||||
velocity.y = jump_velocity
|
||||
_jump_buffer = 0
|
||||
_jump_buffer = 0.0
|
||||
|
||||
# --- 相对视线方向的水平输入 ---
|
||||
# 只用 rig 的 basis(只有 yaw),低头看地时前进方向不会跟着往下扎。
|
||||
var input_dir := Input.get_vector("move_left", "move_right", "move_back", "move_forward")
|
||||
var look_basis := camera_rig.global_transform.basis
|
||||
var wish := look_basis.x * input_dir.x + (-look_basis.z) * input_dir.y
|
||||
var wish := look_basis.x * input_dir.x - look_basis.z * input_dir.y
|
||||
wish.y = 0.0
|
||||
|
||||
var wants_move := wish.length() > 0.001
|
||||
if wants_move:
|
||||
if wish.length_squared() > 0.000001:
|
||||
wish = wish.normalized()
|
||||
# 身体朝向由 _apply_look() 跟着视线走 —— 第一人称不再朝移动方向转身
|
||||
|
||||
var sprinting := Input.is_action_pressed("sprint")
|
||||
# 抱着东西跑不快:禁疾跑 + 整体降速,参数在 @export 里
|
||||
if _carried != null and carry_blocks_sprint:
|
||||
sprinting = false
|
||||
var speed := sprint_speed if sprinting else walk_speed
|
||||
if _carried != null:
|
||||
speed *= carry_speed_scale
|
||||
if not wants_move:
|
||||
speed = 0.0
|
||||
# 只有真正按了方向键才允许疾跑(避免原地"疾跑"触发跑动动画)
|
||||
var sprinting := Input.is_action_pressed("sprint") and input_dir.length_squared() > 0.000001
|
||||
var target_speed := sprint_speed if sprinting else walk_speed
|
||||
var accel := acceleration if is_on_floor() else air_acceleration
|
||||
|
||||
var horizontal := Vector2(velocity.x, velocity.z)
|
||||
horizontal = horizontal.move_toward(Vector2(wish.x, wish.z) * speed, accel * delta)
|
||||
horizontal = horizontal.move_toward(Vector2(wish.x, wish.z) * target_speed, accel * delta)
|
||||
velocity.x = horizontal.x
|
||||
velocity.z = horizontal.y
|
||||
|
||||
@@ -313,193 +136,20 @@ func _physics_process(delta: float) -> void:
|
||||
sprinting,
|
||||
Input.is_action_pressed("crouch"),
|
||||
)
|
||||
|
||||
# 物体拖动
|
||||
if _pickObj != null:
|
||||
var pickObjPos = _pickObj.global_position
|
||||
var pickPlacePos = interaction_pos.global_position
|
||||
_pickObj.set_linear_velocity((pickPlacePos - pickObjPos) * _pickPower)
|
||||
|
||||
|
||||
# ---------------- 抱起 ----------------
|
||||
# 三件事:找物品(_find_carryable)、抱起来(_pick_up)、扔出去(_release_throw)。
|
||||
# 谁能被抱、抱持偏移、投掷速度全在 Carryable 组件上,这里只读不定义 —— 和滑索一个套路。
|
||||
func _pickObject() -> void:
|
||||
var pickCollider = interaction_ray.get_collider()
|
||||
print(pickCollider)
|
||||
if pickCollider != null and pickCollider is RigidBody3D:
|
||||
print(pickCollider)
|
||||
_pickObj = pickCollider
|
||||
|
||||
## 找最近能抱的物品。够不着、或者已经被人抱着的都不算。
|
||||
## 提示文案和左键共用这一套判定,避免「提示亮着却抱不起来」。
|
||||
func _find_carryable() -> Carryable:
|
||||
var best: Carryable = null
|
||||
var best_distance := pickup_radius
|
||||
for node in get_tree().get_nodes_in_group(Carryable.GROUP):
|
||||
var item := node as Carryable
|
||||
if item == null or not item.can_be_carried():
|
||||
continue
|
||||
var distance := global_position.distance_to(item.global_position)
|
||||
if distance > best_distance:
|
||||
continue
|
||||
best_distance = distance
|
||||
best = item
|
||||
return best
|
||||
|
||||
|
||||
## 抱起最近的可抱物品。手上已经有东西、或者正挂在缆上都不抱。
|
||||
## 实际「谁抱到了」由服务器仲裁(先到先得),这里只是发起请求并乐观地先认领,
|
||||
## 服务器不给就在 _physics_process 里超时松手。
|
||||
func _pick_up() -> void:
|
||||
if _carried != null or _zipline != null:
|
||||
return
|
||||
var item := _find_carryable()
|
||||
if item == null:
|
||||
return
|
||||
_carried = item
|
||||
_pickup_wait = pickup_confirm_time
|
||||
if multiplayer.is_server():
|
||||
item.request_pickup(multiplayer.get_unique_id())
|
||||
else:
|
||||
item.request_pickup.rpc_id(1, multiplayer.get_unique_id())
|
||||
|
||||
|
||||
## 松手扔出:方向就是视线方向,速度按蓄力插值(轻点也能扔出去,只是近)。
|
||||
## 跑着扔会把自己当前的速度也叠上,所以助跑能扔更远。
|
||||
func _release_throw() -> void:
|
||||
var item := _carried
|
||||
_carried = null
|
||||
_throw_charging = false
|
||||
if item == null:
|
||||
_throw_charge = 0.0
|
||||
return
|
||||
var launch := -camera.global_transform.basis.z * item.throw_speed(_throw_charge)
|
||||
if throw_inherits_velocity:
|
||||
launch += velocity
|
||||
_throw_charge = 0.0
|
||||
# 出手速度交给服务器:它收回模拟权,让物品带着这个速度接着飞
|
||||
if multiplayer.is_server():
|
||||
item.request_release(launch)
|
||||
else:
|
||||
item.request_release.rpc_id(1, launch)
|
||||
|
||||
|
||||
# ---------------- 滑索 ----------------
|
||||
# 拆成三件事:找缆(_find_zipline)、挂上(_attach_zipline)、缆上运动(_ride_zipline)。
|
||||
# 缆的几何与手感参数都在 Zipline 组件里,这里只管「人」的状态,参数只读不定义。
|
||||
|
||||
## 找脚下能挂的缆,找不到返回 null。挂索与「按 E」提示共用同一套判定。
|
||||
func _find_zipline() -> Zipline:
|
||||
var best: Zipline = null
|
||||
var best_distance := INF
|
||||
for node in get_tree().get_nodes_in_group(Zipline.GROUP):
|
||||
var zip := node as Zipline
|
||||
if zip == null or zip.length <= 0.0:
|
||||
continue
|
||||
var info := zip.closest_point(global_position)
|
||||
# 两种情况都不挂:够不着,或者挂上就没几米可滑(后者会让人刚挂上就被弹下来)
|
||||
if info.distance > zip.attach_radius or info.remaining < zip.min_ride:
|
||||
continue
|
||||
if info.distance < best_distance:
|
||||
best_distance = info.distance
|
||||
best = zip
|
||||
return best
|
||||
|
||||
|
||||
## 挂上最近的一条缆,人就近吸附到缆上最近一点。抱着东西时不给挂(会腾不出手)。
|
||||
func _attach_zipline() -> void:
|
||||
if _carried != null:
|
||||
return
|
||||
var zip := _find_zipline()
|
||||
if zip == null:
|
||||
return
|
||||
var info := zip.closest_point(global_position)
|
||||
_zipline = zip
|
||||
_zip_t = info.t
|
||||
_zip_dir = info.dir
|
||||
# 带上入缆时的水平速度,冲上去挂索不会突然一顿
|
||||
_zip_speed = maxf(Vector2(velocity.x, velocity.z).length() * 0.5, 2.0)
|
||||
# 把当前偏移记下来交给 _ride_zipline 逐帧衰减,避免瞬移到缆上
|
||||
_zip_grab_offset = global_position - zip.ride_point(_zip_t)
|
||||
velocity = Vector3.ZERO
|
||||
|
||||
|
||||
## 脱离缆索。hop=true 时额外给一个向上的小跳(主动跳离用)。
|
||||
func _detach_zipline(hop: bool = false) -> void:
|
||||
if _zipline == null:
|
||||
return
|
||||
# 惯性带走:沿缆方向的速度原样保留,脱手后就是一次抛物线
|
||||
velocity = _zipline.tangent(_zip_t) * float(_zip_dir) * _zip_speed
|
||||
if hop:
|
||||
velocity += Vector3.UP * 3.0
|
||||
_zipline = null
|
||||
_zip_speed = 0.0
|
||||
|
||||
|
||||
## 缆上运动:沿曲线加速下滑,S 刹车,到头自动脱手。
|
||||
##
|
||||
## 位置直接写 global_position,不走 move_and_slide():悬空缆段上没有需要解算的碰撞,
|
||||
## 让物理引擎去推反而会和「贴着曲线走」这个硬约束打架。
|
||||
## velocity 仍然维护 —— 一是给动画器读,二是脱手后能带着惯性飞出去。
|
||||
func _ride_zipline(delta: float) -> void:
|
||||
var tan_dir := _zipline.tangent(_zip_t) * float(_zip_dir)
|
||||
# 重力沿缆方向的分量:缆越陡加速越猛,平缆上几乎不加速。
|
||||
# 下坡时 tan_dir.y 是负的,所以前面要加负号。
|
||||
_zip_speed += -_gravity * _zipline.gravity_scale * tan_dir.y * delta
|
||||
if Input.is_action_pressed("move_back"):
|
||||
_zip_speed -= _zipline.brake_accel * delta
|
||||
_zip_speed = clampf(_zip_speed, 0.0, _zipline.max_speed)
|
||||
|
||||
# t 是曲线参数而不是弧长,但 sag 相对缆长很小时两者差别可以忽略
|
||||
var next_t := _zip_t + float(_zip_dir) * _zip_speed / _zipline.length * delta
|
||||
if next_t <= 0.0 or next_t >= 1.0:
|
||||
# 到头了:先落到端点再脱手,免得停在离端点还差一截的地方
|
||||
_zip_t = clampf(next_t, 0.0, 1.0)
|
||||
global_position = _zipline.ride_point(_zip_t)
|
||||
_detach_zipline()
|
||||
return
|
||||
|
||||
_zip_t = next_t
|
||||
_zip_grab_offset = _zip_grab_offset.lerp(Vector3.ZERO, clampf(delta / 0.12, 0.0, 1.0))
|
||||
global_position = _zipline.ride_point(_zip_t) + _zip_grab_offset
|
||||
velocity = tan_dir * _zip_speed
|
||||
# 视线跟着摆到行进方向 —— 否则人是横着飞的,看不见前面要去哪。
|
||||
# 速率刻意放慢:玩家自己用鼠标看的视角不会被一下子拽回去。
|
||||
_yaw = lerp_angle(_yaw, atan2(-tan_dir.x, -tan_dir.z), clampf(3.0 * delta, 0.0, 1.0))
|
||||
_apply_look()
|
||||
# 缆上没有「落地」概念,交给动画器当下落姿态处理(模型里没有专门的滑索片段)
|
||||
animator.set_motion(velocity, false, false)
|
||||
|
||||
|
||||
func _refresh_health_label() -> void:
|
||||
# 血条节点在 player.tscn 里(Hud/HealthLabel)。
|
||||
# 同步可能在 _ready() 之前就把 health 写进来,那时 _health_label 还是 null,直接跳过。
|
||||
if _health_label == null:
|
||||
return
|
||||
_health_label.text = "HP %d / 100" % health
|
||||
|
||||
|
||||
## 关卡 0 的临时自检:把「我这一端」和「这个节点」分层打印出来。
|
||||
## 联机框架调通后可以整段删掉(连同 _ready() 里的那一行调用)。
|
||||
func _report_identity() -> void:
|
||||
var me: int = multiplayer.get_unique_id()
|
||||
var authority: int = get_multiplayer_authority()
|
||||
|
||||
# 传输层类型:以后换成 Steam 时这里会变成 SteamMultiplayerPeer
|
||||
var transport := "无"
|
||||
if multiplayer.multiplayer_peer != null:
|
||||
transport = multiplayer.multiplayer_peer.get_class()
|
||||
|
||||
# 同步配置:字段为空或未绑定是「远端玩家僵在原地」的头号原因,所以顺手打出来
|
||||
var sync := get_node_or_null("Sync") as MultiplayerSynchronizer
|
||||
var fields := "未绑定"
|
||||
if sync != null and sync.replication_config != null:
|
||||
fields = str(sync.replication_config.get_properties())
|
||||
|
||||
print("""┌─ [节点自检] %s
|
||||
│ 进程级 我是 peer %d | 服务器?%s | 传输 %s | 我知道的 peer:%s
|
||||
│ 节点级 全路径 %s
|
||||
│ 节点名 %s | 归属 peer %d | 归我管?%s
|
||||
│ 同步 Sync 字段 %s
|
||||
└─ 结论 %s""" % [
|
||||
name,
|
||||
me,
|
||||
multiplayer.is_server(),
|
||||
transport,
|
||||
multiplayer.get_peers(),
|
||||
get_path(),
|
||||
name,
|
||||
authority,
|
||||
is_multiplayer_authority(),
|
||||
fields,
|
||||
"这是【我操控】的角色" if is_multiplayer_authority() else "这是【别人】的角色,我只负责回放",
|
||||
])
|
||||
func _removePickObject() -> void:
|
||||
_pickObj = null
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
[ext_resource type="AnimationLibrary" uid="uid://dy8a178vlbsof" path="res://character/player/assets/animations/sneak.glb" id="8_jo6ux"]
|
||||
[ext_resource type="AnimationLibrary" uid="uid://db83440o2cyr7" path="res://character/player/assets/animations/walk.glb" id="9_fkbv7"]
|
||||
[ext_resource type="Script" uid="uid://c6l2fl5jgvr1h" path="res://character/player/face/face_rig.gd" id="12_lqp71"]
|
||||
[ext_resource type="Script" uid="uid://cca1hl0g2bku" path="res://character/player/carry_arms.gd" id="13_carryarms"]
|
||||
|
||||
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_rkbax"]
|
||||
radius = 0.54541016
|
||||
@@ -32,8 +31,12 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.6, 0)
|
||||
[node name="Camera" type="Camera3D" parent="CameraRig" unique_id=52294557]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.6066488, -0.11179829)
|
||||
|
||||
[node name="CarrySocket" type="Marker3D" parent="CameraRig" unique_id=570180284]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.8, -1)
|
||||
[node name="InteractionRay" type="RayCast3D" parent="CameraRig/Camera" unique_id=388601766]
|
||||
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, 0, 0, 0)
|
||||
target_position = Vector3(0, 0.45, 0)
|
||||
|
||||
[node name="InteractionPos" type="Marker3D" parent="CameraRig/Camera" unique_id=816323614]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -0.44393378)
|
||||
|
||||
[node name="Body" type="Node3D" parent="." unique_id=394283421]
|
||||
|
||||
@@ -51,30 +54,8 @@ libraries/sneak = ExtResource("8_jo6ux")
|
||||
libraries/walk = ExtResource("9_fkbv7")
|
||||
script = ExtResource("3_g20gq")
|
||||
|
||||
[node name="CarryArms" type="Node" parent="Body" unique_id=301629134]
|
||||
process_priority = 100
|
||||
script = ExtResource("13_carryarms")
|
||||
|
||||
[node name="Sync" type="MultiplayerSynchronizer" parent="." unique_id=1979575210]
|
||||
replication_config = ExtResource("3_wu8e1")
|
||||
|
||||
[node name="HealthLabel" type="Label3D" parent="." unique_id=300336561]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 2.05, 0)
|
||||
billboard = 1
|
||||
no_depth_test = true
|
||||
text = "HP 100 / 100"
|
||||
font_size = 64
|
||||
outline_size = 8
|
||||
|
||||
[node name="InteractHint" type="Label3D" parent="." unique_id=1718788495]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.75, 0)
|
||||
visible = false
|
||||
billboard = 1
|
||||
no_depth_test = true
|
||||
modulate = Color(1, 0.95, 0.6, 1)
|
||||
text = "按 E 挂上滑索"
|
||||
font_size = 48
|
||||
outline_size = 8
|
||||
|
||||
[node name="FaceRig" type="Node" parent="." unique_id=792293233]
|
||||
script = ExtResource("12_lqp71")
|
||||
|
||||
@@ -36,7 +36,7 @@ project/assembly_name="agame"
|
||||
|
||||
[editor_plugins]
|
||||
|
||||
enabled=PackedStringArray("res://addons/godot_ai/plugin.cfg", "res://addons/godotsteam/plugin.cfg", "res://addons/mesh_collider/plugin.cfg")
|
||||
enabled=PackedStringArray("res://addons/godot_ai/plugin.cfg", "res://addons/godotsteam/plugin.cfg", "res://addons/mesh_collider/plugin.cfg", "res://addons/sky_3d/plugin.cfg")
|
||||
|
||||
[input]
|
||||
|
||||
|
||||
@@ -0,0 +1,261 @@
|
||||
[gd_scene load_steps=17 format=3 uid="uid://c5friqigqdh4k"]
|
||||
|
||||
[ext_resource type="PackedScene" uid="uid://dipn6t27p0nap" path="res://demo/assets/scenes/PrimitivesCity.tscn" id="2"]
|
||||
[ext_resource type="Script" uid="uid://deo0gxkmk2ahd" path="res://addons/sky_3d/src/Sky3D.gd" id="2_cejat"]
|
||||
[ext_resource type="Script" uid="uid://cjmkcj8hwm7ts" path="res://addons/sky_3d/src/TimeOfDay.gd" id="3"]
|
||||
[ext_resource type="Script" uid="uid://b4tkplvh7fq40" path="res://addons/sky_3d/src/SkyDome.gd" id="4"]
|
||||
[ext_resource type="Shader" uid="uid://cvd0x7lon1ur6" path="res://addons/sky_3d/shaders/SkyMaterial.gdshader" id="4_v8wrf"]
|
||||
[ext_resource type="Texture2D" uid="uid://c1vwcdcdvb74a" path="res://addons/sky_3d/assets/thirdparty/textures/milkyway/Milkyway.jpg" id="5_0j4pb"]
|
||||
[ext_resource type="Texture2D" uid="uid://cecwdqjol0ckd" path="res://addons/sky_3d/assets/textures/noiseClouds.png" id="5_rt0jr"]
|
||||
[ext_resource type="Texture2D" uid="uid://cfqk60lpl5ljv" path="res://addons/sky_3d/assets/resources/SNoise.tres" id="6_1g8sw"]
|
||||
[ext_resource type="Texture2D" uid="uid://2r8ylu6rg5dp" path="res://addons/sky_3d/assets/thirdparty/textures/moon/MoonMap.png" id="6_v8wrf"]
|
||||
[ext_resource type="Texture2D" uid="uid://djpfuyxkryegn" path="res://addons/sky_3d/assets/textures/noise.jpg" id="7_d0ed4"]
|
||||
[ext_resource type="Texture2D" uid="uid://bm7dot7t7u1q4" path="res://addons/sky_3d/assets/thirdparty/textures/milkyway/StarField.jpg" id="8_humls"]
|
||||
|
||||
[sub_resource type="GDScript" id="GDScript_v8wrf"]
|
||||
script/source = "extends Node3D
|
||||
|
||||
## Combination Player and Camera Manager
|
||||
## Included as a scene script (a bad practice) so it does not conflict with the Terrain3D demo when
|
||||
## used together.
|
||||
|
||||
@export var move_speed: float = 60.0
|
||||
|
||||
const CAMERA_MAX_PITCH: float = deg_to_rad(70)
|
||||
const CAMERA_MIN_PITCH: float = deg_to_rad(-89.9)
|
||||
const CAMERA_RATIO: float = .625
|
||||
|
||||
@export var mouse_sensitivity: float = .002
|
||||
@export var mouse_y_inversion: float = -1.0
|
||||
|
||||
@onready var _camera_yaw: Node3D = self
|
||||
@onready var _camera_pitch: Node3D = $Camera3D
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
RenderingServer.set_debug_generate_wireframes(true)
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
|
||||
|
||||
|
||||
func _physics_process(p_delta: float) -> void:
|
||||
position += get_camera_relative_input().normalized() * move_speed * p_delta
|
||||
|
||||
|
||||
func _input(p_event: InputEvent) -> void:
|
||||
if p_event is InputEventMouseMotion and Input.get_mouse_mode() == Input.MOUSE_MODE_CAPTURED:
|
||||
rotate_camera(p_event.relative)
|
||||
get_viewport().set_input_as_handled()
|
||||
return
|
||||
if p_event is InputEventMouseButton and p_event.pressed:
|
||||
if p_event.button_index == MOUSE_BUTTON_WHEEL_UP:
|
||||
$Camera3D.fov = max($Camera3D.fov - 2., 5.)
|
||||
elif p_event.button_index == MOUSE_BUTTON_WHEEL_DOWN:
|
||||
$Camera3D.fov = min($Camera3D.fov + 2., 160.)
|
||||
return
|
||||
if p_event is InputEventKey and p_event.pressed:
|
||||
match p_event.keycode:
|
||||
KEY_F8:
|
||||
get_tree().quit()
|
||||
KEY_F10:
|
||||
var vp: Viewport = get_viewport()
|
||||
vp.debug_draw = (vp.debug_draw + 1 ) % 6
|
||||
get_viewport().set_input_as_handled()
|
||||
KEY_F11:
|
||||
toggle_fullscreen()
|
||||
get_viewport().set_input_as_handled()
|
||||
KEY_ESCAPE, KEY_F12:
|
||||
if Input.get_mouse_mode() == Input.MOUSE_MODE_VISIBLE:
|
||||
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
|
||||
else:
|
||||
Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
|
||||
get_viewport().set_input_as_handled()
|
||||
|
||||
|
||||
# Returns the input vector relative to the camera. Forward is always the direction the camera is facing
|
||||
func get_camera_relative_input() -> Vector3:
|
||||
var input_dir: Vector3 = Vector3.ZERO
|
||||
if Input.is_key_pressed(KEY_A): # Left
|
||||
input_dir -= global_transform.basis.x
|
||||
if Input.is_key_pressed(KEY_D): # Right
|
||||
input_dir += global_transform.basis.x
|
||||
if Input.is_key_pressed(KEY_W): # Forward
|
||||
input_dir -= global_transform.basis.z
|
||||
if Input.is_key_pressed(KEY_S): # Backward
|
||||
input_dir += global_transform.basis.z
|
||||
if Input.is_key_pressed(KEY_E) or Input.is_key_pressed(KEY_SPACE): # Up
|
||||
input_dir += global_transform.basis.y
|
||||
if Input.is_key_pressed(KEY_Q): # Down
|
||||
input_dir -= global_transform.basis.y
|
||||
if Input.is_key_pressed(KEY_KP_ADD) or Input.is_key_pressed(KEY_EQUAL):
|
||||
move_speed = clamp(move_speed + .5, 5, 9999)
|
||||
if Input.is_key_pressed(KEY_KP_SUBTRACT) or Input.is_key_pressed(KEY_MINUS):
|
||||
move_speed = clamp(move_speed - .5, 5, 9999)
|
||||
return input_dir
|
||||
|
||||
|
||||
func rotate_camera(p_relative:Vector2) -> void:
|
||||
_camera_yaw.rotation.y -= p_relative.x * mouse_sensitivity
|
||||
_camera_yaw.orthonormalize()
|
||||
_camera_pitch.rotation.x += p_relative.y * mouse_sensitivity * CAMERA_RATIO * mouse_y_inversion
|
||||
_camera_pitch.rotation.x = clamp(_camera_pitch.rotation.x, CAMERA_MIN_PITCH, CAMERA_MAX_PITCH)
|
||||
|
||||
|
||||
func toggle_fullscreen() -> void:
|
||||
if DisplayServer.window_get_mode() == DisplayServer.WINDOW_MODE_EXCLUSIVE_FULLSCREEN or \\
|
||||
DisplayServer.window_get_mode() == DisplayServer.WINDOW_MODE_FULLSCREEN:
|
||||
DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED)
|
||||
DisplayServer.window_set_size(Vector2(1280, 720))
|
||||
else:
|
||||
DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_EXCLUSIVE_FULLSCREEN)
|
||||
"
|
||||
|
||||
[sub_resource type="ShaderMaterial" id="ShaderMaterial_v8wrf"]
|
||||
shader = ExtResource("4_v8wrf")
|
||||
shader_parameter/sky_visible = true
|
||||
shader_parameter/color_correction = Vector2(0, 1)
|
||||
shader_parameter/ground_color = Color(0.3, 0.3, 0.3, 1)
|
||||
shader_parameter/horizon_offset = 0.0
|
||||
shader_parameter/atm_darkness = 0.5
|
||||
shader_parameter/atm_sun_intensity = 18.0
|
||||
shader_parameter/atm_day_tint = Color(0.807843, 0.909804, 1, 1)
|
||||
shader_parameter/atm_horizon_light_tint = Color(0.980392, 0.635294, 0.462745, 1)
|
||||
shader_parameter/atm_night_tint = Color(0.057391796, 0.068069525, 0.085420445, 0.34034762)
|
||||
shader_parameter/atm_level_params = Vector3(1, 0, 0)
|
||||
shader_parameter/atm_thickness = 0.7
|
||||
shader_parameter/atm_beta_ray = Vector3(5.804544e-06, 1.3562913e-05, 3.311258e-05)
|
||||
shader_parameter/atm_beta_mie = Vector3(3.038e-08, 3.038e-08, 3.038e-08)
|
||||
shader_parameter/sun_light_color = Color(0.98, 0.523, 0.294, 1)
|
||||
shader_parameter/sun_disk_color = Color(0.996094, 0.541334, 0.140076, 1)
|
||||
shader_parameter/sun_disk_intensity = 30.0
|
||||
shader_parameter/sun_disk_size = 0.02
|
||||
shader_parameter/atm_sun_mie_tint = Color(1, 1, 1, 1)
|
||||
shader_parameter/atm_sun_mie_intensity = 1.0
|
||||
shader_parameter/atm_sun_partial_mie_phase = Vector3(0.36, 1.64, 1.6)
|
||||
shader_parameter/moon_color = Color(1, 1, 1, 1)
|
||||
shader_parameter/moon_texture = ExtResource("6_v8wrf")
|
||||
shader_parameter/moon_texture_alignment = Vector3(7, 1.4, 4.8)
|
||||
shader_parameter/moon_texture_flip_u = false
|
||||
shader_parameter/moon_texture_flip_v = false
|
||||
shader_parameter/moon_size = 0.07
|
||||
shader_parameter/atm_moon_mie_tint = Color(0.137255, 0.184314, 0.292196, 1)
|
||||
shader_parameter/atm_moon_mie_intensity = 0.23658217810094354
|
||||
shader_parameter/atm_moon_partial_mie_phase = Vector3(0.36, 1.64, 1.6)
|
||||
shader_parameter/starmap_color = Color(0.709804, 0.709804, 0.709804, 0.854902)
|
||||
shader_parameter/starmap_texture = ExtResource("5_0j4pb")
|
||||
shader_parameter/starmap_flip_u = false
|
||||
shader_parameter/starmap_flip_v = false
|
||||
shader_parameter/starmap_alignment = Vector3(2.68288, -0.25891, 0.40101)
|
||||
shader_parameter/star_rotation_offset = 9.38899
|
||||
shader_parameter/star_rotation = -9.711138284154199
|
||||
shader_parameter/star_tilt = -1.2915436464758039
|
||||
shader_parameter/star_field_color = Color(1, 1, 1, 1)
|
||||
shader_parameter/star_field_texture = ExtResource("8_humls")
|
||||
shader_parameter/star_scintillation = 0.75
|
||||
shader_parameter/star_scintillation_speed = 0.01
|
||||
shader_parameter/noise_tex = ExtResource("7_d0ed4")
|
||||
shader_parameter/clouds_night_color = Color(0.090196, 0.094118, 0.129412, 1)
|
||||
shader_parameter/cirrus_visible = true
|
||||
shader_parameter/cirrus_intensity = 2.0
|
||||
shader_parameter/cirrus_coverage = 0.5
|
||||
shader_parameter/cirrus_thickness = 1.7
|
||||
shader_parameter/cirrus_absorption = 2.0
|
||||
shader_parameter/cirrus_sky_tint_fade = 0.5
|
||||
shader_parameter/cirrus_size = 1.0
|
||||
shader_parameter/cirrus_uv = Vector2(0.16, 0.11)
|
||||
shader_parameter/cirrus_position1 = Vector2(0.7308965, 5.0460643e-05)
|
||||
shader_parameter/cirrus_position2 = Vector2(0.7308965, 5.0460643e-05)
|
||||
shader_parameter/cirrus_texture = ExtResource("6_1g8sw")
|
||||
shader_parameter/cumulus_visible = true
|
||||
shader_parameter/cumulus_intensity = 0.6
|
||||
shader_parameter/cumulus_coverage = 0.55
|
||||
shader_parameter/cumulus_thickness = 0.0243
|
||||
shader_parameter/cumulus_absorption = 2.0
|
||||
shader_parameter/cumulus_noise_freq = 2.7
|
||||
shader_parameter/cumulus_sky_tint_fade = 0.0
|
||||
shader_parameter/cumulus_size = 0.5
|
||||
shader_parameter/cumulus_position = Vector2(79.19066, 0.00029048725)
|
||||
shader_parameter/cumulus_texture = ExtResource("5_rt0jr")
|
||||
shader_parameter/cumulus_partial_mie_phase = Vector3(0.957564, 1.042436, 0.412)
|
||||
shader_parameter/cumulus_mie_intensity = 1.0
|
||||
shader_parameter/show_azimuthal_grid = false
|
||||
shader_parameter/azimuthal_grid_color = Color(0.87058824, 0.72156864, 0.5294118, 1)
|
||||
shader_parameter/azimuthal_grid_rotation_offset = 0.03
|
||||
shader_parameter/show_equatorial_grid = false
|
||||
shader_parameter/equatorial_grid_color = Color(0, 0.75, 1, 1)
|
||||
shader_parameter/equatorial_grid_rotation_offset = 0.03
|
||||
shader_parameter/moon_matrix = Basis(-7.4505815e-09, -0.27634087, 0.9610598, -0.9609947, -0.2657975, -0.0764268, 0.2765671, -0.9235733, -0.26556212)
|
||||
|
||||
[sub_resource type="Sky" id="Sky_0j4pb"]
|
||||
sky_material = SubResource("ShaderMaterial_v8wrf")
|
||||
|
||||
[sub_resource type="Environment" id="Environment_1g8sw"]
|
||||
background_mode = 2
|
||||
sky = SubResource("Sky_0j4pb")
|
||||
ambient_light_source = 3
|
||||
ambient_light_color = Color(0.38921005, 0.23168632, 0.16043657, 1)
|
||||
ambient_light_sky_contribution = 0.7
|
||||
reflected_light_source = 2
|
||||
tonemap_mode = 3
|
||||
tonemap_white = 6.0
|
||||
|
||||
[sub_resource type="CameraAttributesPractical" id="CameraAttributesPractical_d0ed4"]
|
||||
|
||||
[node name="Main" type="Node3D"]
|
||||
|
||||
[node name="PrimitivesCity" parent="." instance=ExtResource("2")]
|
||||
|
||||
[node name="CameraManager" type="Node3D" parent="."]
|
||||
transform = Transform3D(0.0801989, 0, -0.996779, 0, 1, 0, 0.996779, 0, 0.0801989, -7.677, 4, 3.686)
|
||||
script = SubResource("GDScript_v8wrf")
|
||||
|
||||
[node name="Camera3D" type="Camera3D" parent="CameraManager"]
|
||||
transform = Transform3D(1, 0, 0, 0, 0.987688, 0.156434, 0, -0.156434, 0.987688, 0, 0, 0)
|
||||
fov = 70.0
|
||||
far = 8192.0
|
||||
|
||||
[node name="ReflectionProbe" type="ReflectionProbe" parent="."]
|
||||
update_mode = 1
|
||||
blend_distance = 8.0
|
||||
size = Vector3(2000, 2000, 2000)
|
||||
origin_offset = Vector3(0, 5, 0)
|
||||
|
||||
[node name="Sky3D" type="WorldEnvironment" parent="."]
|
||||
environment = SubResource("Environment_1g8sw")
|
||||
camera_attributes = SubResource("CameraAttributesPractical_d0ed4")
|
||||
script = ExtResource("2_cejat")
|
||||
current_time = 6.15
|
||||
wind_speed = 7.0
|
||||
wind_direction = 1.5708
|
||||
metadata/_custom_type_script = "uid://deo0gxkmk2ahd"
|
||||
|
||||
[node name="SunLight" type="DirectionalLight3D" parent="Sky3D"]
|
||||
transform = Transform3D(0.3947903, 0.037070245, 0.9180231, 0, 0.9991857, -0.040347632, -0.91877127, 0.015928853, 0.3944688, 0.9180231, -0.040347632, 0.3944688)
|
||||
light_color = Color(0.98, 0.523, 0.294, 1)
|
||||
light_energy = 0.0
|
||||
directional_shadow_blend_splits = true
|
||||
directional_shadow_max_distance = 600.0
|
||||
|
||||
[node name="MoonLight" type="DirectionalLight3D" parent="Sky3D"]
|
||||
transform = Transform3D(0, -0.9609946, 0.27656707, -0.27634087, -0.26579747, -0.9235732, 0.9610597, -0.07642679, -0.2655621, 0.27656707, -0.9235732, -0.2655621)
|
||||
light_color = Color(0.572549, 0.776471, 0.956863, 1)
|
||||
light_energy = 0.0
|
||||
directional_shadow_blend_splits = true
|
||||
directional_shadow_max_distance = 256.0
|
||||
|
||||
[node name="SkyDome" type="Node" parent="Sky3D"]
|
||||
script = ExtResource("4")
|
||||
sun_azimuth = -1.9766359
|
||||
sun_altitude = -1.6111549
|
||||
moon_azimuth = -0.80569494
|
||||
moon_altitude = -2.7480938
|
||||
fog_density = 0.01
|
||||
wind_speed = 7.0
|
||||
wind_direction = 1.5708
|
||||
|
||||
[node name="TimeOfDay" type="Node" parent="Sky3D"]
|
||||
script = ExtResource("3")
|
||||
dome_path = NodePath("../SkyDome")
|
||||
current_time = 6.15
|
||||
@@ -0,0 +1,3 @@
|
||||
[gd_resource type="CameraAttributesPractical" format=3 uid="uid://c41dvxjh8vfeb"]
|
||||
|
||||
[resource]
|
||||
@@ -0,0 +1,76 @@
|
||||
[gd_resource type="Environment" load_steps=9 format=3 uid="uid://dlo4d1tui6x2k"]
|
||||
|
||||
[ext_resource type="Shader" uid="uid://mjo3i43h0fwm" path="res://addons/sky_3d/shaders/Sky.gdshader" id="1_scbay"]
|
||||
[ext_resource type="Texture2D" uid="uid://c1vwcdcdvb74a" path="res://addons/sky_3d/assets/thirdparty/textures/milkyway/Milkyway.jpg" id="2_iuqa4"]
|
||||
[ext_resource type="Texture2D" uid="uid://cfqk60lpl5ljv" path="res://addons/sky_3d/assets/resources/SNoise.tres" id="3_073gb"]
|
||||
[ext_resource type="Texture2D" uid="uid://djpfuyxkryegn" path="res://addons/sky_3d/assets/textures/noise.jpg" id="4_go3mt"]
|
||||
[ext_resource type="Texture2D" uid="uid://bm7dot7t7u1q4" path="res://addons/sky_3d/assets/thirdparty/textures/milkyway/StarField.jpg" id="5_nk2sh"]
|
||||
|
||||
[sub_resource type="ViewportTexture" id="ViewportTexture_scbay"]
|
||||
viewport_path = NodePath(".")
|
||||
|
||||
[sub_resource type="ShaderMaterial" id="ShaderMaterial_568mj"]
|
||||
render_priority = -128
|
||||
shader = ExtResource("1_scbay")
|
||||
shader_parameter/_color_correction_params = Vector2(0, 1.3)
|
||||
shader_parameter/_ground_color = Color(0.3, 0.3, 0.3, 1)
|
||||
shader_parameter/_horizon_level = 0.0
|
||||
shader_parameter/_sun_direction = Vector3(-0.760075, 0.626975, -0.170846)
|
||||
shader_parameter/_moon_direction = Vector3(-0.489545, -0.867954, 0.0836778)
|
||||
shader_parameter/_moon_size = 0.07
|
||||
shader_parameter/_moon_matrix = Basis(0, 0.0959632, 0.995385, -0.871978, 0.487286, -0.0469783, -0.489545, -0.867954, 0.0836778)
|
||||
shader_parameter/_deep_space_matrix = Basis(0.333882, -0.942615, 0, -0.259821, -0.0920306, -0.961262, 0.906099, 0.320948, -0.275638)
|
||||
shader_parameter/_atm_darkness = 0.5
|
||||
shader_parameter/_atm_sun_intensity = 18.0
|
||||
shader_parameter/_atm_day_tint = Color(0.807843, 0.909804, 1, 1)
|
||||
shader_parameter/_atm_horizon_light_tint = Color(0.980392, 0.635294, 0.462745, 1)
|
||||
shader_parameter/_atm_night_tint = Color(0, 0, 0, 0)
|
||||
shader_parameter/_atm_level_params = Vector3(1, 0, 0)
|
||||
shader_parameter/_atm_thickness = 0.7
|
||||
shader_parameter/_atm_sun_mie_tint = Color(1, 1, 1, 1)
|
||||
shader_parameter/_atm_sun_mie_intensity = 1.0
|
||||
shader_parameter/_atm_moon_mie_tint = Color(0.137255, 0.184314, 0.292196, 1)
|
||||
shader_parameter/_atm_moon_mie_intensity = 0.0
|
||||
shader_parameter/_atm_beta_ray = Vector3(5.80454e-06, 1.35629e-05, 3.31126e-05)
|
||||
shader_parameter/_atm_beta_mie = Vector3(3.038e-08, 3.038e-08, 3.038e-08)
|
||||
shader_parameter/_atm_sun_partial_mie_phase = Vector3(0.36, 1.64, 1.6)
|
||||
shader_parameter/_atm_moon_partial_mie_phase = Vector3(0.36, 1.64, 1.6)
|
||||
shader_parameter/_sun_disk_color = Color(0.996094, 0.541334, 0.140076, 1)
|
||||
shader_parameter/_sun_disk_intensity = 15.0
|
||||
shader_parameter/_sun_disk_size = 0.02
|
||||
shader_parameter/_moon_color = Color(1, 1, 1, 1)
|
||||
shader_parameter/_moon_texture = SubResource("ViewportTexture_scbay")
|
||||
shader_parameter/_background_texture = ExtResource("2_iuqa4")
|
||||
shader_parameter/_background_color = Color(0.709804, 0.709804, 0.709804, 0.854902)
|
||||
shader_parameter/_stars_field_color = Color(1, 1, 1, 1)
|
||||
shader_parameter/_stars_field_texture = ExtResource("5_nk2sh")
|
||||
shader_parameter/_stars_scintillation = 0.75
|
||||
shader_parameter/_stars_scintillation_speed = 0.01
|
||||
shader_parameter/_noise_tex = ExtResource("4_go3mt")
|
||||
shader_parameter/_clouds_coverage = 0.627
|
||||
shader_parameter/_clouds_thickness = 1.7
|
||||
shader_parameter/_clouds_absorption = 2.0
|
||||
shader_parameter/_clouds_sky_tint_fade = 0.5
|
||||
shader_parameter/_clouds_intensity = 10.0
|
||||
shader_parameter/_clouds_size = 2.0
|
||||
shader_parameter/_clouds_uv = Vector2(0.16, 0.11)
|
||||
shader_parameter/_clouds_speed = 0.07
|
||||
shader_parameter/_clouds_direction = Vector2(0.25, 0.25)
|
||||
shader_parameter/_clouds_day_color = Color(0.823529, 0.87451, 1, 1)
|
||||
shader_parameter/_clouds_horizon_light_color = Color(0.98, 0.4312, 0.1568, 1)
|
||||
shader_parameter/_clouds_night_color = Color(0.090196, 0.094118, 0.129412, 1)
|
||||
shader_parameter/_clouds_texture = ExtResource("3_073gb")
|
||||
|
||||
[sub_resource type="Sky" id="Sky_cwklf"]
|
||||
sky_material = SubResource("ShaderMaterial_568mj")
|
||||
|
||||
[resource]
|
||||
background_mode = 2
|
||||
sky = SubResource("Sky_cwklf")
|
||||
ambient_light_source = 3
|
||||
ambient_light_color = Color(1, 1, 1, 1)
|
||||
ambient_light_sky_contribution = 0.85
|
||||
reflected_light_source = 2
|
||||
tonemap_mode = 3
|
||||
tonemap_white = 6.0
|
||||
ssil_enabled = true
|
||||
@@ -0,0 +1,91 @@
|
||||
# Blender v2.90.0 OBJ File: ''
|
||||
# www.blender.org
|
||||
mtllib Block.mtl
|
||||
o Beam
|
||||
v 0.250024 0.500000 -1.000003
|
||||
v -0.249976 0.500000 -1.000003
|
||||
v -0.149976 0.400000 -1.000003
|
||||
v 0.150024 0.400000 -1.000003
|
||||
v -0.249976 -0.500000 -1.000003
|
||||
v -0.149976 -0.400000 -1.000003
|
||||
v 0.250024 -0.500000 -1.000003
|
||||
v 0.150024 -0.400000 -1.000003
|
||||
v 0.250024 0.500000 0.999997
|
||||
v -0.249976 0.500000 0.999997
|
||||
v -0.149976 0.400000 0.999997
|
||||
v 0.150024 0.400000 0.999997
|
||||
v -0.249976 -0.500000 0.999997
|
||||
v -0.149976 -0.400000 0.999997
|
||||
v 0.250024 -0.500000 0.999997
|
||||
v 0.150024 -0.400000 0.999997
|
||||
v 0.250024 0.500000 -1.000003
|
||||
v -0.249976 0.500000 -1.000003
|
||||
v -0.249976 0.500000 0.999997
|
||||
v 0.250024 0.500000 0.999997
|
||||
v -0.249976 0.500000 -1.000003
|
||||
v -0.249976 -0.500000 -1.000003
|
||||
v -0.249976 -0.500000 0.999997
|
||||
v -0.249976 0.500000 0.999997
|
||||
v -0.249976 -0.500000 -1.000003
|
||||
v 0.250024 -0.500000 -1.000003
|
||||
v 0.250024 -0.500000 0.999997
|
||||
v -0.249976 -0.500000 0.999997
|
||||
v 0.250024 -0.500000 -1.000003
|
||||
v 0.250024 0.500000 -1.000003
|
||||
v 0.250024 0.500000 0.999997
|
||||
v 0.250024 -0.500000 0.999997
|
||||
v 0.150024 0.400000 -1.000003
|
||||
v -0.149976 0.400000 -1.000003
|
||||
v -0.149976 0.400000 0.999997
|
||||
v 0.150024 0.400000 0.999997
|
||||
v -0.149976 0.400000 -1.000003
|
||||
v -0.149976 -0.400000 -1.000003
|
||||
v -0.149976 -0.400000 0.999997
|
||||
v -0.149976 0.400000 0.999997
|
||||
v -0.149976 -0.400000 -1.000003
|
||||
v 0.150024 -0.400000 -1.000003
|
||||
v 0.150024 -0.400000 0.999997
|
||||
v -0.149976 -0.400000 0.999997
|
||||
v 0.150024 -0.400000 -1.000003
|
||||
v 0.150024 0.400000 -1.000003
|
||||
v 0.150024 0.400000 0.999997
|
||||
v 0.150024 -0.400000 0.999997
|
||||
vn 0.0000 0.0000 1.0000
|
||||
vn 0.0000 1.0000 0.0000
|
||||
vn -1.0000 0.0000 0.0000
|
||||
vn 0.0000 -1.0000 0.0000
|
||||
vn 1.0000 0.0000 0.0000
|
||||
usemtl None
|
||||
s off
|
||||
f 1//1 2//1 3//1
|
||||
f 1//1 3//1 4//1
|
||||
f 2//1 5//1 6//1
|
||||
f 2//1 6//1 3//1
|
||||
f 5//1 7//1 8//1
|
||||
f 5//1 8//1 6//1
|
||||
f 7//1 1//1 4//1
|
||||
f 7//1 4//1 8//1
|
||||
f 9//1 10//1 11//1
|
||||
f 9//1 11//1 12//1
|
||||
f 10//1 13//1 14//1
|
||||
f 10//1 14//1 11//1
|
||||
f 13//1 15//1 16//1
|
||||
f 13//1 16//1 14//1
|
||||
f 15//1 9//1 12//1
|
||||
f 15//1 12//1 16//1
|
||||
f 17//2 18//2 19//2
|
||||
f 17//2 19//2 20//2
|
||||
f 21//3 22//3 23//3
|
||||
f 21//3 23//3 24//3
|
||||
f 25//4 26//4 27//4
|
||||
f 25//4 27//4 28//4
|
||||
f 29//5 30//5 31//5
|
||||
f 29//5 31//5 32//5
|
||||
f 33//2 34//2 35//2
|
||||
f 33//2 35//2 36//2
|
||||
f 37//3 38//3 39//3
|
||||
f 37//3 39//3 40//3
|
||||
f 41//4 42//4 43//4
|
||||
f 41//4 43//4 44//4
|
||||
f 45//5 46//5 47//5
|
||||
f 45//5 47//5 48//5
|
||||
@@ -0,0 +1,25 @@
|
||||
[remap]
|
||||
|
||||
importer="wavefront_obj"
|
||||
importer_version=1
|
||||
type="Mesh"
|
||||
uid="uid://2pl3vwyawlw6"
|
||||
path="res://.godot/imported/Block.obj-2619e0a6ce4e9c9e09c3826ba019bb7e.mesh"
|
||||
|
||||
[deps]
|
||||
|
||||
files=["res://.godot/imported/Block.obj-2619e0a6ce4e9c9e09c3826ba019bb7e.mesh"]
|
||||
|
||||
source_file="res://sky_demo/assets/meshes/Block.obj"
|
||||
dest_files=["res://.godot/imported/Block.obj-2619e0a6ce4e9c9e09c3826ba019bb7e.mesh", "res://.godot/imported/Block.obj-2619e0a6ce4e9c9e09c3826ba019bb7e.mesh"]
|
||||
|
||||
[params]
|
||||
|
||||
generate_tangents=true
|
||||
generate_lods=true
|
||||
generate_shadow_mesh=true
|
||||
generate_lightmap_uv2=false
|
||||
generate_lightmap_uv2_texel_size=0.2
|
||||
scale_mesh=Vector3(1, 1, 1)
|
||||
offset_mesh=Vector3(0, 0, 0)
|
||||
force_disable_mesh_compression=false
|
||||
@@ -0,0 +1,68 @@
|
||||
[gd_scene load_steps=3 format=3 uid="uid://1ev4o7q8l4ey"]
|
||||
|
||||
[sub_resource type="BoxMesh" id="BoxMesh_f03xp"]
|
||||
size = Vector3(4, 0.25, 0.25)
|
||||
|
||||
[sub_resource type="PrismMesh" id="PrismMesh_duxrp"]
|
||||
size = Vector3(1, 1, 0.25)
|
||||
|
||||
[node name="Compass" type="Node3D"]
|
||||
|
||||
[node name="BaseNS" type="MeshInstance3D" parent="."]
|
||||
transform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, 0, 0, 0)
|
||||
mesh = SubResource("BoxMesh_f03xp")
|
||||
skeleton = NodePath("../BaseEW")
|
||||
|
||||
[node name="ArrowHeadN" type="MeshInstance3D" parent="BaseNS"]
|
||||
transform = Transform3D(-4.37114e-08, 1, 4.37114e-08, 0, -4.37114e-08, 1, 1, 4.37114e-08, 1.91069e-15, 2.5, 0, 4.37114e-08)
|
||||
mesh = SubResource("PrismMesh_duxrp")
|
||||
skeleton = NodePath("../..")
|
||||
|
||||
[node name="Label3DN" type="Label3D" parent="BaseNS/ArrowHeadN"]
|
||||
transform = Transform3D(1, 0, -6.87826e-36, 0, 1, 0, 0, 0, 1, 0, 0, 0.35)
|
||||
billboard = 1
|
||||
double_sided = false
|
||||
text = "N"
|
||||
font_size = 96
|
||||
|
||||
[node name="ArrowHeadS" type="MeshInstance3D" parent="BaseNS"]
|
||||
transform = Transform3D(-4.37114e-08, -1, -4.37114e-08, 0, -4.37114e-08, 1, -1, 4.37114e-08, 1.91069e-15, -2.5, 0, -4.37114e-08)
|
||||
mesh = SubResource("PrismMesh_duxrp")
|
||||
skeleton = NodePath("../..")
|
||||
|
||||
[node name="Label3DS" type="Label3D" parent="BaseNS/ArrowHeadS"]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0.35)
|
||||
billboard = 1
|
||||
double_sided = false
|
||||
no_depth_test = true
|
||||
text = "S"
|
||||
font_size = 96
|
||||
|
||||
[node name="BaseEW" type="MeshInstance3D" parent="."]
|
||||
mesh = SubResource("BoxMesh_f03xp")
|
||||
|
||||
[node name="ArrowHeadE" type="MeshInstance3D" parent="BaseEW"]
|
||||
transform = Transform3D(-4.37114e-08, 1, 0, 4.37114e-08, 1.91068e-15, 1, 1, 4.37114e-08, -4.37114e-08, 2.5, 0, 0)
|
||||
mesh = SubResource("PrismMesh_duxrp")
|
||||
skeleton = NodePath("../..")
|
||||
|
||||
[node name="Label3DE" type="Label3D" parent="BaseEW/ArrowHeadE"]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0.35)
|
||||
billboard = 1
|
||||
double_sided = false
|
||||
no_depth_test = true
|
||||
text = "E"
|
||||
font_size = 96
|
||||
|
||||
[node name="ArrowHeadW" type="MeshInstance3D" parent="BaseEW"]
|
||||
transform = Transform3D(-4.37114e-08, -1, 0, -4.37114e-08, 1.91069e-15, 1, -1, 4.37114e-08, -4.37114e-08, -2.5, 0, 0)
|
||||
mesh = SubResource("PrismMesh_duxrp")
|
||||
skeleton = NodePath("../..")
|
||||
|
||||
[node name="Label3DW" type="Label3D" parent="BaseEW/ArrowHeadW"]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0.35)
|
||||
billboard = 1
|
||||
double_sided = false
|
||||
no_depth_test = true
|
||||
text = "W"
|
||||
font_size = 96
|
||||
@@ -0,0 +1,25 @@
|
||||
[remap]
|
||||
|
||||
importer="wavefront_obj"
|
||||
importer_version=1
|
||||
type="Mesh"
|
||||
uid="uid://b2r1duervowul"
|
||||
path="res://.godot/imported/Diamond.obj-c075ea64421e65dd7ab238b06801163e.mesh"
|
||||
|
||||
[deps]
|
||||
|
||||
files=["res://.godot/imported/Diamond.obj-c075ea64421e65dd7ab238b06801163e.mesh"]
|
||||
|
||||
source_file="res://sky_demo/assets/meshes/Diamond.obj"
|
||||
dest_files=["res://.godot/imported/Diamond.obj-c075ea64421e65dd7ab238b06801163e.mesh", "res://.godot/imported/Diamond.obj-c075ea64421e65dd7ab238b06801163e.mesh"]
|
||||
|
||||
[params]
|
||||
|
||||
generate_tangents=true
|
||||
generate_lods=true
|
||||
generate_shadow_mesh=true
|
||||
generate_lightmap_uv2=false
|
||||
generate_lightmap_uv2_texel_size=0.2
|
||||
scale_mesh=Vector3(1, 1, 1)
|
||||
offset_mesh=Vector3(0, 0, 0)
|
||||
force_disable_mesh_compression=false
|
||||
@@ -0,0 +1,25 @@
|
||||
[remap]
|
||||
|
||||
importer="wavefront_obj"
|
||||
importer_version=1
|
||||
type="Mesh"
|
||||
uid="uid://cl032y2ung7i7"
|
||||
path="res://.godot/imported/Gear.obj-d6bef984e4add58e26ad6f78bcbe350b.mesh"
|
||||
|
||||
[deps]
|
||||
|
||||
files=["res://.godot/imported/Gear.obj-d6bef984e4add58e26ad6f78bcbe350b.mesh"]
|
||||
|
||||
source_file="res://sky_demo/assets/meshes/Gear.obj"
|
||||
dest_files=["res://.godot/imported/Gear.obj-d6bef984e4add58e26ad6f78bcbe350b.mesh", "res://.godot/imported/Gear.obj-d6bef984e4add58e26ad6f78bcbe350b.mesh"]
|
||||
|
||||
[params]
|
||||
|
||||
generate_tangents=true
|
||||
generate_lods=true
|
||||
generate_shadow_mesh=true
|
||||
generate_lightmap_uv2=false
|
||||
generate_lightmap_uv2_texel_size=0.2
|
||||
scale_mesh=Vector3(1, 1, 1)
|
||||
offset_mesh=Vector3(0, 0, 0)
|
||||
force_disable_mesh_compression=false
|
||||
@@ -0,0 +1,257 @@
|
||||
# Blender v2.90.0 OBJ File: ''
|
||||
# www.blender.org
|
||||
mtllib Gem.mtl
|
||||
o Gem
|
||||
v 0.382707 -0.400000 -0.382686
|
||||
v 0.000024 -0.800000 -0.000003
|
||||
v 0.000024 -0.400000 -0.541199
|
||||
v 0.382707 0.000000 -0.923882
|
||||
v 0.382707 -0.400000 -0.382686
|
||||
v 0.382707 0.000000 -0.923882
|
||||
v 0.923903 0.000000 -0.382686
|
||||
v 0.923903 0.000000 -0.382686
|
||||
v 0.382707 0.000000 -0.923882
|
||||
v 0.612317 0.175000 -0.612296
|
||||
v 0.612317 0.175000 -0.612296
|
||||
v 0.382707 0.000000 -0.923882
|
||||
v 0.000024 0.175000 -0.865916
|
||||
v 0.229634 0.350000 -0.554330
|
||||
v 0.612317 0.175000 -0.612296
|
||||
v 0.229634 0.350000 -0.554330
|
||||
v 0.554351 0.350000 -0.229613
|
||||
v 0.541220 -0.400000 -0.000003
|
||||
v 0.000024 -0.800000 -0.000003
|
||||
v 0.382707 -0.400000 -0.382686
|
||||
v 0.923903 0.000000 -0.382686
|
||||
v 0.541220 -0.400000 -0.000003
|
||||
v 0.923903 0.000000 -0.382686
|
||||
v 0.923903 0.000000 0.382681
|
||||
v 0.923903 0.000000 0.382681
|
||||
v 0.923903 0.000000 -0.382686
|
||||
v 0.865937 0.175000 -0.000003
|
||||
v 0.865937 0.175000 -0.000003
|
||||
v 0.923903 0.000000 -0.382686
|
||||
v 0.612317 0.175000 -0.612296
|
||||
v 0.554351 0.350000 -0.229613
|
||||
v 0.865937 0.175000 -0.000003
|
||||
v 0.554351 0.350000 -0.229613
|
||||
v 0.554351 0.350000 0.229607
|
||||
v 0.382707 -0.400000 0.382681
|
||||
v 0.000024 -0.800000 -0.000003
|
||||
v 0.541220 -0.400000 -0.000003
|
||||
v 0.923903 0.000000 0.382681
|
||||
v 0.382707 -0.400000 0.382681
|
||||
v 0.923903 0.000000 0.382681
|
||||
v 0.382707 0.000000 0.923877
|
||||
v 0.382707 0.000000 0.923877
|
||||
v 0.923903 0.000000 0.382681
|
||||
v 0.612317 0.175000 0.612291
|
||||
v 0.612317 0.175000 0.612291
|
||||
v 0.923903 0.000000 0.382681
|
||||
v 0.865937 0.175000 -0.000003
|
||||
v 0.554351 0.350000 0.229607
|
||||
v 0.612317 0.175000 0.612291
|
||||
v 0.554351 0.350000 0.229607
|
||||
v 0.229634 0.350000 0.554325
|
||||
v 0.000024 -0.400000 0.541194
|
||||
v 0.000024 -0.800000 -0.000003
|
||||
v 0.382707 -0.400000 0.382681
|
||||
v 0.382707 0.000000 0.923877
|
||||
v 0.000024 -0.400000 0.541194
|
||||
v 0.382707 0.000000 0.923877
|
||||
v -0.382660 0.000000 0.923877
|
||||
v -0.382660 0.000000 0.923877
|
||||
v 0.382707 0.000000 0.923877
|
||||
v 0.000024 0.175000 0.865911
|
||||
v 0.000024 0.175000 0.865911
|
||||
v 0.382707 0.000000 0.923877
|
||||
v 0.612317 0.175000 0.612291
|
||||
v 0.229634 0.350000 0.554325
|
||||
v 0.000024 0.175000 0.865911
|
||||
v 0.229634 0.350000 0.554325
|
||||
v -0.229586 0.350000 0.554325
|
||||
v -0.382660 -0.400000 0.382681
|
||||
v 0.000024 -0.800000 -0.000003
|
||||
v 0.000024 -0.400000 0.541194
|
||||
v -0.382660 0.000000 0.923877
|
||||
v -0.382660 -0.400000 0.382681
|
||||
v -0.382660 0.000000 0.923877
|
||||
v -0.923856 0.000000 0.382681
|
||||
v -0.923856 0.000000 0.382681
|
||||
v -0.382660 0.000000 0.923877
|
||||
v -0.612270 0.175000 0.612291
|
||||
v -0.612270 0.175000 0.612291
|
||||
v -0.382660 0.000000 0.923877
|
||||
v 0.000024 0.175000 0.865911
|
||||
v -0.229586 0.350000 0.554325
|
||||
v -0.612270 0.175000 0.612291
|
||||
v -0.229586 0.350000 0.554325
|
||||
v -0.554304 0.350000 0.229607
|
||||
v -0.541172 -0.400000 -0.000003
|
||||
v 0.000024 -0.800000 -0.000003
|
||||
v -0.382660 -0.400000 0.382681
|
||||
v -0.923856 0.000000 0.382681
|
||||
v -0.541172 -0.400000 -0.000003
|
||||
v -0.923856 0.000000 0.382681
|
||||
v -0.923856 0.000000 -0.382686
|
||||
v -0.923856 0.000000 -0.382686
|
||||
v -0.923856 0.000000 0.382681
|
||||
v -0.865890 0.175000 -0.000003
|
||||
v -0.865890 0.175000 -0.000003
|
||||
v -0.923856 0.000000 0.382681
|
||||
v -0.612270 0.175000 0.612291
|
||||
v -0.554304 0.350000 0.229607
|
||||
v -0.865890 0.175000 -0.000003
|
||||
v -0.554304 0.350000 0.229607
|
||||
v -0.554304 0.350000 -0.229613
|
||||
v -0.382660 -0.400000 -0.382686
|
||||
v 0.000024 -0.800000 -0.000003
|
||||
v -0.541172 -0.400000 -0.000003
|
||||
v -0.923856 0.000000 -0.382686
|
||||
v -0.382660 -0.400000 -0.382686
|
||||
v -0.923856 0.000000 -0.382686
|
||||
v -0.382660 0.000000 -0.923882
|
||||
v -0.382660 0.000000 -0.923882
|
||||
v -0.923856 0.000000 -0.382686
|
||||
v -0.612270 0.175000 -0.612296
|
||||
v -0.612270 0.175000 -0.612296
|
||||
v -0.923856 0.000000 -0.382686
|
||||
v -0.865890 0.175000 -0.000003
|
||||
v -0.554304 0.350000 -0.229613
|
||||
v -0.612270 0.175000 -0.612296
|
||||
v -0.554304 0.350000 -0.229613
|
||||
v -0.229586 0.350000 -0.554330
|
||||
v 0.000024 -0.400000 -0.541199
|
||||
v 0.000024 -0.800000 -0.000003
|
||||
v -0.382660 -0.400000 -0.382686
|
||||
v -0.382660 0.000000 -0.923882
|
||||
v 0.000024 -0.400000 -0.541199
|
||||
v -0.382660 0.000000 -0.923882
|
||||
v 0.382707 0.000000 -0.923882
|
||||
v 0.382707 0.000000 -0.923882
|
||||
v -0.382660 0.000000 -0.923882
|
||||
v 0.000024 0.175000 -0.865916
|
||||
v 0.000024 0.175000 -0.865916
|
||||
v -0.382660 0.000000 -0.923882
|
||||
v -0.612270 0.175000 -0.612296
|
||||
v -0.229586 0.350000 -0.554330
|
||||
v 0.000024 0.175000 -0.865916
|
||||
v -0.229586 0.350000 -0.554330
|
||||
v 0.229634 0.350000 -0.554330
|
||||
v 0.229634 0.350000 -0.554330
|
||||
v 0.000024 0.350000 -0.000003
|
||||
v -0.229586 0.350000 -0.554330
|
||||
v 0.554351 0.350000 -0.229613
|
||||
v 0.554351 0.350000 0.229607
|
||||
v 0.229634 0.350000 0.554325
|
||||
v -0.229586 0.350000 0.554325
|
||||
v -0.554304 0.350000 0.229607
|
||||
v -0.554304 0.350000 -0.229613
|
||||
vn 0.2391 -0.7809 -0.5771
|
||||
vn 0.5109 -0.6913 -0.5109
|
||||
vn 0.6712 0.3144 -0.6712
|
||||
vn 0.2520 0.7526 -0.6084
|
||||
vn 0.3463 0.8719 -0.3463
|
||||
vn 0.5771 -0.7809 -0.2391
|
||||
vn 0.9493 0.3144 0.0000
|
||||
vn 0.6084 0.7526 -0.2520
|
||||
vn 0.4897 0.8719 0.0000
|
||||
vn 0.5771 -0.7809 0.2391
|
||||
vn 0.5109 -0.6913 0.5109
|
||||
vn 0.6712 0.3144 0.6712
|
||||
vn 0.6084 0.7526 0.2520
|
||||
vn 0.3463 0.8719 0.3463
|
||||
vn 0.2391 -0.7809 0.5771
|
||||
vn 0.0000 -0.6913 0.7226
|
||||
vn 0.0000 0.3144 0.9493
|
||||
vn 0.2520 0.7526 0.6084
|
||||
vn 0.0000 0.8719 0.4897
|
||||
vn -0.2391 -0.7809 0.5771
|
||||
vn -0.5109 -0.6913 0.5109
|
||||
vn -0.6712 0.3144 0.6712
|
||||
vn -0.2520 0.7526 0.6084
|
||||
vn -0.3463 0.8719 0.3463
|
||||
vn -0.5771 -0.7809 0.2391
|
||||
vn -0.9493 0.3144 0.0000
|
||||
vn -0.6084 0.7526 0.2520
|
||||
vn -0.4897 0.8719 0.0000
|
||||
vn -0.5771 -0.7809 -0.2391
|
||||
vn -0.5109 -0.6913 -0.5109
|
||||
vn -0.6712 0.3144 -0.6712
|
||||
vn -0.6084 0.7526 -0.2520
|
||||
vn -0.3463 0.8719 -0.3463
|
||||
vn -0.2391 -0.7809 -0.5771
|
||||
vn 0.0000 -0.6913 -0.7226
|
||||
vn 0.0000 0.3144 -0.9493
|
||||
vn -0.2520 0.7526 -0.6084
|
||||
vn 0.0000 0.8719 -0.4897
|
||||
vn 0.0000 -1.0000 0.0000
|
||||
vn 0.7226 -0.6913 0.0000
|
||||
vn -0.7226 -0.6913 0.0000
|
||||
usemtl None
|
||||
s off
|
||||
f 1//1 2//1 3//1
|
||||
f 1//1 3//1 4//1
|
||||
f 5//2 6//2 7//2
|
||||
f 8//3 9//3 10//3
|
||||
f 11//4 12//4 13//4
|
||||
f 11//4 13//4 14//4
|
||||
f 15//5 16//5 17//5
|
||||
f 18//6 19//6 20//6
|
||||
f 18//6 20//6 21//6
|
||||
f 25//7 26//7 27//7
|
||||
f 28//8 29//8 30//8
|
||||
f 28//8 30//8 31//8
|
||||
f 32//9 33//9 34//9
|
||||
f 35//10 36//10 37//10
|
||||
f 35//10 37//10 38//10
|
||||
f 39//11 40//11 41//11
|
||||
f 42//12 43//12 44//12
|
||||
f 45//13 46//13 47//13
|
||||
f 45//13 47//13 48//13
|
||||
f 49//14 50//14 51//14
|
||||
f 52//15 53//15 54//15
|
||||
f 52//15 54//15 55//15
|
||||
f 56//16 57//16 58//16
|
||||
f 59//17 60//17 61//17
|
||||
f 62//18 63//18 64//18
|
||||
f 62//18 64//18 65//18
|
||||
f 66//19 67//19 68//19
|
||||
f 69//20 70//20 71//20
|
||||
f 69//20 71//20 72//20
|
||||
f 73//21 74//21 75//21
|
||||
f 76//22 77//22 78//22
|
||||
f 79//23 80//23 81//23
|
||||
f 79//23 81//23 82//23
|
||||
f 83//24 84//24 85//24
|
||||
f 86//25 87//25 88//25
|
||||
f 86//25 88//25 89//25
|
||||
f 93//26 94//26 95//26
|
||||
f 96//27 97//27 98//27
|
||||
f 96//27 98//27 99//27
|
||||
f 100//28 101//28 102//28
|
||||
f 103//29 104//29 105//29
|
||||
f 103//29 105//29 106//29
|
||||
f 107//30 108//30 109//30
|
||||
f 110//31 111//31 112//31
|
||||
f 113//32 114//32 115//32
|
||||
f 113//32 115//32 116//32
|
||||
f 117//33 118//33 119//33
|
||||
f 120//34 121//34 122//34
|
||||
f 120//34 122//34 123//34
|
||||
f 124//35 125//35 126//35
|
||||
f 127//36 128//36 129//36
|
||||
f 130//37 131//37 132//37
|
||||
f 130//37 132//37 133//37
|
||||
f 134//38 135//38 136//38
|
||||
f 137//39 138//39 139//39
|
||||
f 138//39 137//39 140//39
|
||||
f 138//39 140//39 141//39
|
||||
f 138//39 141//39 142//39
|
||||
f 138//39 142//39 143//39
|
||||
f 138//39 143//39 144//39
|
||||
f 138//39 144//39 145//39
|
||||
f 138//39 145//39 139//39
|
||||
s 1
|
||||
f 22//40 23//40 24//40
|
||||
f 90//41 91//41 92//41
|
||||
@@ -0,0 +1,25 @@
|
||||
[remap]
|
||||
|
||||
importer="wavefront_obj"
|
||||
importer_version=1
|
||||
type="Mesh"
|
||||
uid="uid://c6qb0mg7ygxa0"
|
||||
path="res://.godot/imported/Gem.obj-3ec0852bc70a6ea5972ee3dd66d42a1d.mesh"
|
||||
|
||||
[deps]
|
||||
|
||||
files=["res://.godot/imported/Gem.obj-3ec0852bc70a6ea5972ee3dd66d42a1d.mesh"]
|
||||
|
||||
source_file="res://sky_demo/assets/meshes/Gem.obj"
|
||||
dest_files=["res://.godot/imported/Gem.obj-3ec0852bc70a6ea5972ee3dd66d42a1d.mesh", "res://.godot/imported/Gem.obj-3ec0852bc70a6ea5972ee3dd66d42a1d.mesh"]
|
||||
|
||||
[params]
|
||||
|
||||
generate_tangents=true
|
||||
generate_lods=true
|
||||
generate_shadow_mesh=true
|
||||
generate_lightmap_uv2=false
|
||||
generate_lightmap_uv2_texel_size=0.2
|
||||
scale_mesh=Vector3(1, 1, 1)
|
||||
offset_mesh=Vector3(0, 0, 0)
|
||||
force_disable_mesh_compression=false
|
||||
@@ -0,0 +1,481 @@
|
||||
# Blender v2.90.0 OBJ File: ''
|
||||
# www.blender.org
|
||||
mtllib Honey.mtl
|
||||
o HoneyComb
|
||||
v -1.515521 0.000000 1.790412
|
||||
v -1.550521 0.000000 1.729790
|
||||
v -1.948533 0.000000 1.959583
|
||||
v -1.913533 0.000000 2.020205
|
||||
v -1.983533 0.000000 2.020205
|
||||
v -0.649495 0.000000 1.790412
|
||||
v -0.684495 0.000000 1.729790
|
||||
v -1.082508 0.000000 1.959583
|
||||
v -1.047508 0.000000 2.020205
|
||||
v -1.117508 0.000000 2.020205
|
||||
v 0.216530 0.000000 1.790412
|
||||
v 0.181530 0.000000 1.729790
|
||||
v -0.216483 0.000000 1.959583
|
||||
v -0.181483 0.000000 2.020205
|
||||
v -0.251483 0.000000 2.020205
|
||||
v 1.082556 0.000000 1.790412
|
||||
v 1.047555 0.000000 1.729790
|
||||
v 0.649543 0.000000 1.959583
|
||||
v 0.684543 0.000000 2.020205
|
||||
v 0.614543 0.000000 2.020205
|
||||
v 1.948581 0.000000 1.790412
|
||||
v 1.913581 0.000000 1.729790
|
||||
v 1.515568 0.000000 1.959583
|
||||
v 1.550568 0.000000 2.020205
|
||||
v 1.480568 0.000000 2.020205
|
||||
v -1.913533 0.000000 -2.020210
|
||||
v -1.983533 0.000000 -2.020210
|
||||
v -1.948533 0.000000 -1.959588
|
||||
v -1.550521 0.000000 -1.729795
|
||||
v -1.515521 0.000000 -1.790417
|
||||
v -1.047508 0.000000 -2.020210
|
||||
v -1.117508 0.000000 -2.020210
|
||||
v -1.082508 0.000000 -1.959588
|
||||
v -0.684495 0.000000 -1.729795
|
||||
v -0.649495 0.000000 -1.790417
|
||||
v -0.181483 0.000000 -2.020210
|
||||
v -0.251483 0.000000 -2.020210
|
||||
v -0.216483 0.000000 -1.959588
|
||||
v 0.181530 0.000000 -1.729795
|
||||
v 0.216530 0.000000 -1.790417
|
||||
v 0.684543 0.000000 -2.020210
|
||||
v 0.614543 0.000000 -2.020210
|
||||
v 0.649543 0.000000 -1.959588
|
||||
v 1.047555 0.000000 -1.729795
|
||||
v 1.082556 0.000000 -1.790417
|
||||
v 1.550568 0.000000 -2.020210
|
||||
v 1.480568 0.000000 -2.020210
|
||||
v 1.515568 0.000000 -1.959588
|
||||
v 1.913581 0.000000 -1.729795
|
||||
v 1.948581 0.000000 -1.790417
|
||||
v -1.948533 0.000000 1.040412
|
||||
v -1.983533 0.000000 0.979790
|
||||
v -2.381546 0.000000 1.209583
|
||||
v -2.346546 0.000000 1.270205
|
||||
v -2.416546 0.000000 1.270205
|
||||
v -2.416546 0.000000 1.729790
|
||||
v -2.346546 0.000000 1.729790
|
||||
v -2.381546 0.000000 1.790412
|
||||
v -1.082508 0.000000 1.040412
|
||||
v -1.117508 0.000000 0.979790
|
||||
v -1.515521 0.000000 1.209583
|
||||
v -1.480521 0.000000 1.270205
|
||||
v -1.550521 0.000000 1.270205
|
||||
v -1.480521 0.000000 1.729790
|
||||
v -0.216483 0.000000 1.040412
|
||||
v -0.251483 0.000000 0.979790
|
||||
v -0.649495 0.000000 1.209583
|
||||
v -0.614495 0.000000 1.270205
|
||||
v -0.684495 0.000000 1.270205
|
||||
v -0.614495 0.000000 1.729790
|
||||
v 0.649543 0.000000 1.040412
|
||||
v 0.614543 0.000000 0.979790
|
||||
v 0.216530 0.000000 1.209583
|
||||
v 0.251530 0.000000 1.270205
|
||||
v 0.181530 0.000000 1.270205
|
||||
v 0.251530 0.000000 1.729790
|
||||
v 1.515568 0.000000 1.040412
|
||||
v 1.480568 0.000000 0.979790
|
||||
v 1.082556 0.000000 1.209583
|
||||
v 1.117555 0.000000 1.270205
|
||||
v 1.047555 0.000000 1.270205
|
||||
v 1.117555 0.000000 1.729790
|
||||
v -1.515521 0.000000 0.290412
|
||||
v -1.550521 0.000000 0.229790
|
||||
v -1.948533 0.000000 0.459583
|
||||
v -1.913533 0.000000 0.520205
|
||||
v -1.983533 0.000000 0.520205
|
||||
v -1.913533 0.000000 0.979790
|
||||
v -0.649495 0.000000 0.290412
|
||||
v -0.684495 0.000000 0.229790
|
||||
v -1.082508 0.000000 0.459583
|
||||
v -1.047508 0.000000 0.520205
|
||||
v -1.117508 0.000000 0.520205
|
||||
v -1.047508 0.000000 0.979790
|
||||
v 0.216530 0.000000 0.290412
|
||||
v 0.181530 0.000000 0.229790
|
||||
v -0.216483 0.000000 0.459583
|
||||
v -0.181483 0.000000 0.520205
|
||||
v -0.251483 0.000000 0.520205
|
||||
v -0.181483 0.000000 0.979790
|
||||
v 1.082556 0.000000 0.290412
|
||||
v 1.047555 0.000000 0.229790
|
||||
v 0.649543 0.000000 0.459583
|
||||
v 0.684543 0.000000 0.520205
|
||||
v 0.614543 0.000000 0.520205
|
||||
v 0.684543 0.000000 0.979790
|
||||
v 1.948581 0.000000 0.290412
|
||||
v 1.913581 0.000000 0.229790
|
||||
v 1.515568 0.000000 0.459583
|
||||
v 1.550568 0.000000 0.520205
|
||||
v 1.480568 0.000000 0.520205
|
||||
v 1.550568 0.000000 0.979790
|
||||
v 1.913581 0.000000 1.270205
|
||||
v 1.948581 0.000000 1.209583
|
||||
v -1.948533 0.000000 -0.459588
|
||||
v -1.983533 0.000000 -0.520210
|
||||
v -2.381546 0.000000 -0.290417
|
||||
v -2.346546 0.000000 -0.229795
|
||||
v -2.416546 0.000000 -0.229795
|
||||
v -2.416546 0.000000 0.229790
|
||||
v -2.346546 0.000000 0.229790
|
||||
v -2.381546 0.000000 0.290412
|
||||
v -1.082508 0.000000 -0.459588
|
||||
v -1.117508 0.000000 -0.520210
|
||||
v -1.515521 0.000000 -0.290417
|
||||
v -1.480521 0.000000 -0.229795
|
||||
v -1.550521 0.000000 -0.229795
|
||||
v -1.480521 0.000000 0.229790
|
||||
v -0.216483 0.000000 -0.459588
|
||||
v -0.251483 0.000000 -0.520210
|
||||
v -0.649495 0.000000 -0.290417
|
||||
v -0.614495 0.000000 -0.229795
|
||||
v -0.684495 0.000000 -0.229795
|
||||
v -0.614495 0.000000 0.229790
|
||||
v 0.649543 0.000000 -0.459588
|
||||
v 0.614543 0.000000 -0.520210
|
||||
v 0.216530 0.000000 -0.290417
|
||||
v 0.251530 0.000000 -0.229795
|
||||
v 0.251530 0.000000 -0.229795
|
||||
v 0.216530 0.000000 -0.290417
|
||||
v 0.181530 0.000000 -0.229795
|
||||
v 0.181530 0.000000 -0.229795
|
||||
v 0.251530 0.000000 0.229790
|
||||
v 0.251530 0.000000 0.229790
|
||||
v 0.181530 0.000000 0.229790
|
||||
v 0.216530 0.000000 0.290412
|
||||
v 1.515568 0.000000 -0.459588
|
||||
v 1.480568 0.000000 -0.520210
|
||||
v 1.082556 0.000000 -0.290417
|
||||
v 1.117555 0.000000 -0.229795
|
||||
v 1.047555 0.000000 -0.229795
|
||||
v 1.117555 0.000000 0.229790
|
||||
v -1.515521 0.000000 -1.209588
|
||||
v -1.550521 0.000000 -1.270210
|
||||
v -1.948533 0.000000 -1.040417
|
||||
v -1.913533 0.000000 -0.979795
|
||||
v -1.983533 0.000000 -0.979795
|
||||
v -1.913533 0.000000 -0.520210
|
||||
v -0.649495 0.000000 -1.209588
|
||||
v -0.684495 0.000000 -1.270210
|
||||
v -1.082508 0.000000 -1.040417
|
||||
v -1.047508 0.000000 -0.979795
|
||||
v -1.117508 0.000000 -0.979795
|
||||
v -1.047508 0.000000 -0.520210
|
||||
v 0.216530 0.000000 -1.209588
|
||||
v 0.181530 0.000000 -1.270210
|
||||
v -0.216483 0.000000 -1.040417
|
||||
v -0.181483 0.000000 -0.979795
|
||||
v -0.251483 0.000000 -0.979795
|
||||
v -0.181483 0.000000 -0.520210
|
||||
v 1.082556 0.000000 -1.209588
|
||||
v 1.047555 0.000000 -1.270210
|
||||
v 0.649543 0.000000 -1.040417
|
||||
v 0.684543 0.000000 -0.979795
|
||||
v 0.614543 0.000000 -0.979795
|
||||
v 0.684543 0.000000 -0.520210
|
||||
v 1.948581 0.000000 -1.209588
|
||||
v 1.913581 0.000000 -1.270210
|
||||
v 1.515568 0.000000 -1.040417
|
||||
v 1.550568 0.000000 -0.979795
|
||||
v 1.480568 0.000000 -0.979795
|
||||
v 1.550568 0.000000 -0.520210
|
||||
v 1.913581 0.000000 -0.229795
|
||||
v 1.948581 0.000000 -0.290417
|
||||
v -2.381546 0.000000 -1.790417
|
||||
v -2.346546 0.000000 -1.729795
|
||||
v -2.416546 0.000000 -1.729795
|
||||
v -2.416546 0.000000 -1.270210
|
||||
v -2.346546 0.000000 -1.270210
|
||||
v -2.381546 0.000000 -1.209588
|
||||
v -1.480521 0.000000 -1.729795
|
||||
v -1.480521 0.000000 -1.270210
|
||||
v -0.614495 0.000000 -1.729795
|
||||
v -0.614495 0.000000 -1.270210
|
||||
v 0.251530 0.000000 -1.729795
|
||||
v 0.251530 0.000000 -1.270210
|
||||
v 1.117555 0.000000 -1.729795
|
||||
v 1.117555 0.000000 -1.270210
|
||||
v 2.381594 0.000000 1.040412
|
||||
v 2.346594 0.000000 0.979790
|
||||
v 1.983581 0.000000 1.270205
|
||||
v 1.983581 0.000000 1.729790
|
||||
v 2.416594 0.000000 0.520205
|
||||
v 2.381594 0.000000 0.459583
|
||||
v 2.346594 0.000000 0.520205
|
||||
v 2.416594 0.000000 0.979790
|
||||
v 2.381594 0.000000 -0.459588
|
||||
v 2.346594 0.000000 -0.520210
|
||||
v 1.983581 0.000000 -0.229795
|
||||
v 1.983581 0.000000 0.229790
|
||||
v 2.416594 0.000000 -0.979795
|
||||
v 2.381594 0.000000 -1.040417
|
||||
v 2.346594 0.000000 -0.979795
|
||||
v 2.416594 0.000000 -0.520210
|
||||
v 1.983581 0.000000 -1.729795
|
||||
v 1.983581 0.000000 -1.270210
|
||||
vn 0.0000 1.0000 0.0000
|
||||
usemtl None
|
||||
s off
|
||||
f 1//1 2//1 3//1
|
||||
f 1//1 3//1 4//1
|
||||
f 4//1 3//1 5//1
|
||||
f 6//1 7//1 8//1
|
||||
f 6//1 8//1 9//1
|
||||
f 9//1 8//1 10//1
|
||||
f 11//1 12//1 13//1
|
||||
f 11//1 13//1 14//1
|
||||
f 14//1 13//1 15//1
|
||||
f 16//1 17//1 18//1
|
||||
f 16//1 18//1 19//1
|
||||
f 19//1 18//1 20//1
|
||||
f 21//1 22//1 23//1
|
||||
f 21//1 23//1 24//1
|
||||
f 24//1 23//1 25//1
|
||||
f 26//1 27//1 28//1
|
||||
f 26//1 28//1 29//1
|
||||
f 26//1 29//1 30//1
|
||||
f 31//1 32//1 33//1
|
||||
f 31//1 33//1 34//1
|
||||
f 31//1 34//1 35//1
|
||||
f 36//1 37//1 38//1
|
||||
f 36//1 38//1 39//1
|
||||
f 36//1 39//1 40//1
|
||||
f 41//1 42//1 43//1
|
||||
f 41//1 43//1 44//1
|
||||
f 41//1 44//1 45//1
|
||||
f 46//1 47//1 48//1
|
||||
f 46//1 48//1 49//1
|
||||
f 46//1 49//1 50//1
|
||||
f 51//1 52//1 53//1
|
||||
f 51//1 53//1 54//1
|
||||
f 54//1 53//1 55//1
|
||||
f 54//1 55//1 56//1
|
||||
f 54//1 56//1 57//1
|
||||
f 57//1 56//1 58//1
|
||||
f 57//1 58//1 5//1
|
||||
f 57//1 5//1 3//1
|
||||
f 59//1 60//1 61//1
|
||||
f 59//1 61//1 62//1
|
||||
f 62//1 61//1 63//1
|
||||
f 62//1 63//1 2//1
|
||||
f 62//1 2//1 64//1
|
||||
f 64//1 2//1 1//1
|
||||
f 64//1 1//1 10//1
|
||||
f 64//1 10//1 8//1
|
||||
f 65//1 66//1 67//1
|
||||
f 65//1 67//1 68//1
|
||||
f 68//1 67//1 69//1
|
||||
f 68//1 69//1 7//1
|
||||
f 68//1 7//1 70//1
|
||||
f 70//1 7//1 6//1
|
||||
f 70//1 6//1 15//1
|
||||
f 70//1 15//1 13//1
|
||||
f 71//1 72//1 73//1
|
||||
f 71//1 73//1 74//1
|
||||
f 74//1 73//1 75//1
|
||||
f 74//1 75//1 12//1
|
||||
f 74//1 12//1 76//1
|
||||
f 76//1 12//1 11//1
|
||||
f 76//1 11//1 20//1
|
||||
f 76//1 20//1 18//1
|
||||
f 77//1 78//1 79//1
|
||||
f 77//1 79//1 80//1
|
||||
f 80//1 79//1 81//1
|
||||
f 80//1 81//1 17//1
|
||||
f 80//1 17//1 82//1
|
||||
f 82//1 17//1 16//1
|
||||
f 82//1 16//1 25//1
|
||||
f 82//1 25//1 23//1
|
||||
f 83//1 84//1 85//1
|
||||
f 83//1 85//1 86//1
|
||||
f 86//1 85//1 87//1
|
||||
f 86//1 87//1 52//1
|
||||
f 86//1 52//1 88//1
|
||||
f 88//1 52//1 51//1
|
||||
f 88//1 51//1 63//1
|
||||
f 88//1 63//1 61//1
|
||||
f 89//1 90//1 91//1
|
||||
f 89//1 91//1 92//1
|
||||
f 92//1 91//1 93//1
|
||||
f 92//1 93//1 60//1
|
||||
f 92//1 60//1 94//1
|
||||
f 94//1 60//1 59//1
|
||||
f 94//1 59//1 69//1
|
||||
f 94//1 69//1 67//1
|
||||
f 95//1 96//1 97//1
|
||||
f 95//1 97//1 98//1
|
||||
f 98//1 97//1 99//1
|
||||
f 98//1 99//1 66//1
|
||||
f 98//1 66//1 100//1
|
||||
f 100//1 66//1 65//1
|
||||
f 100//1 65//1 75//1
|
||||
f 100//1 75//1 73//1
|
||||
f 101//1 102//1 103//1
|
||||
f 101//1 103//1 104//1
|
||||
f 104//1 103//1 105//1
|
||||
f 104//1 105//1 72//1
|
||||
f 104//1 72//1 106//1
|
||||
f 106//1 72//1 71//1
|
||||
f 106//1 71//1 81//1
|
||||
f 106//1 81//1 79//1
|
||||
f 107//1 108//1 109//1
|
||||
f 107//1 109//1 110//1
|
||||
f 110//1 109//1 111//1
|
||||
f 110//1 111//1 78//1
|
||||
f 110//1 78//1 112//1
|
||||
f 112//1 78//1 77//1
|
||||
f 112//1 77//1 113//1
|
||||
f 112//1 113//1 114//1
|
||||
f 115//1 116//1 117//1
|
||||
f 115//1 117//1 118//1
|
||||
f 118//1 117//1 119//1
|
||||
f 118//1 119//1 120//1
|
||||
f 118//1 120//1 121//1
|
||||
f 121//1 120//1 122//1
|
||||
f 121//1 122//1 87//1
|
||||
f 121//1 87//1 85//1
|
||||
f 123//1 124//1 125//1
|
||||
f 123//1 125//1 126//1
|
||||
f 126//1 125//1 127//1
|
||||
f 126//1 127//1 84//1
|
||||
f 126//1 84//1 128//1
|
||||
f 128//1 84//1 83//1
|
||||
f 128//1 83//1 93//1
|
||||
f 128//1 93//1 91//1
|
||||
f 129//1 130//1 131//1
|
||||
f 129//1 131//1 132//1
|
||||
f 132//1 131//1 133//1
|
||||
f 132//1 133//1 90//1
|
||||
f 132//1 90//1 134//1
|
||||
f 134//1 90//1 89//1
|
||||
f 134//1 89//1 99//1
|
||||
f 134//1 99//1 97//1
|
||||
f 135//1 136//1 137//1
|
||||
f 135//1 137//1 138//1
|
||||
f 139//1 140//1 141//1
|
||||
f 138//1 142//1 96//1
|
||||
f 138//1 96//1 143//1
|
||||
f 144//1 145//1 146//1
|
||||
f 143//1 95//1 105//1
|
||||
f 143//1 105//1 103//1
|
||||
f 147//1 148//1 149//1
|
||||
f 147//1 149//1 150//1
|
||||
f 150//1 149//1 151//1
|
||||
f 150//1 151//1 102//1
|
||||
f 150//1 102//1 152//1
|
||||
f 152//1 102//1 101//1
|
||||
f 152//1 101//1 111//1
|
||||
f 152//1 111//1 109//1
|
||||
f 153//1 154//1 155//1
|
||||
f 153//1 155//1 156//1
|
||||
f 156//1 155//1 157//1
|
||||
f 156//1 157//1 116//1
|
||||
f 156//1 116//1 158//1
|
||||
f 158//1 116//1 115//1
|
||||
f 158//1 115//1 127//1
|
||||
f 158//1 127//1 125//1
|
||||
f 159//1 160//1 161//1
|
||||
f 159//1 161//1 162//1
|
||||
f 162//1 161//1 163//1
|
||||
f 162//1 163//1 124//1
|
||||
f 162//1 124//1 164//1
|
||||
f 164//1 124//1 123//1
|
||||
f 164//1 123//1 133//1
|
||||
f 164//1 133//1 131//1
|
||||
f 165//1 166//1 167//1
|
||||
f 165//1 167//1 168//1
|
||||
f 168//1 167//1 169//1
|
||||
f 168//1 169//1 130//1
|
||||
f 168//1 130//1 170//1
|
||||
f 170//1 130//1 129//1
|
||||
f 170//1 129//1 142//1
|
||||
f 170//1 142//1 137//1
|
||||
f 171//1 172//1 173//1
|
||||
f 171//1 173//1 174//1
|
||||
f 174//1 173//1 175//1
|
||||
f 174//1 175//1 136//1
|
||||
f 174//1 136//1 176//1
|
||||
f 176//1 136//1 135//1
|
||||
f 176//1 135//1 151//1
|
||||
f 176//1 151//1 149//1
|
||||
f 177//1 178//1 179//1
|
||||
f 177//1 179//1 180//1
|
||||
f 180//1 179//1 181//1
|
||||
f 180//1 181//1 148//1
|
||||
f 180//1 148//1 182//1
|
||||
f 182//1 148//1 147//1
|
||||
f 182//1 147//1 183//1
|
||||
f 182//1 183//1 184//1
|
||||
f 28//1 27//1 185//1
|
||||
f 28//1 185//1 186//1
|
||||
f 186//1 185//1 187//1
|
||||
f 186//1 187//1 188//1
|
||||
f 186//1 188//1 189//1
|
||||
f 189//1 188//1 190//1
|
||||
f 189//1 190//1 157//1
|
||||
f 189//1 157//1 155//1
|
||||
f 33//1 32//1 30//1
|
||||
f 33//1 30//1 191//1
|
||||
f 191//1 30//1 29//1
|
||||
f 191//1 29//1 154//1
|
||||
f 191//1 154//1 192//1
|
||||
f 192//1 154//1 153//1
|
||||
f 192//1 153//1 163//1
|
||||
f 192//1 163//1 161//1
|
||||
f 38//1 37//1 35//1
|
||||
f 38//1 35//1 193//1
|
||||
f 193//1 35//1 34//1
|
||||
f 193//1 34//1 160//1
|
||||
f 193//1 160//1 194//1
|
||||
f 194//1 160//1 159//1
|
||||
f 194//1 159//1 169//1
|
||||
f 194//1 169//1 167//1
|
||||
f 43//1 42//1 40//1
|
||||
f 43//1 40//1 195//1
|
||||
f 195//1 40//1 39//1
|
||||
f 195//1 39//1 166//1
|
||||
f 195//1 166//1 196//1
|
||||
f 196//1 166//1 165//1
|
||||
f 196//1 165//1 175//1
|
||||
f 196//1 175//1 173//1
|
||||
f 48//1 47//1 45//1
|
||||
f 48//1 45//1 197//1
|
||||
f 197//1 45//1 44//1
|
||||
f 197//1 44//1 172//1
|
||||
f 197//1 172//1 198//1
|
||||
f 198//1 172//1 171//1
|
||||
f 198//1 171//1 181//1
|
||||
f 198//1 181//1 179//1
|
||||
f 199//1 200//1 114//1
|
||||
f 199//1 114//1 201//1
|
||||
f 201//1 114//1 113//1
|
||||
f 201//1 113//1 22//1
|
||||
f 201//1 22//1 202//1
|
||||
f 202//1 22//1 21//1
|
||||
f 203//1 204//1 205//1
|
||||
f 203//1 205//1 200//1
|
||||
f 203//1 200//1 206//1
|
||||
f 206//1 200//1 199//1
|
||||
f 207//1 208//1 184//1
|
||||
f 207//1 184//1 209//1
|
||||
f 209//1 184//1 183//1
|
||||
f 209//1 183//1 108//1
|
||||
f 209//1 108//1 210//1
|
||||
f 210//1 108//1 107//1
|
||||
f 210//1 107//1 205//1
|
||||
f 210//1 205//1 204//1
|
||||
f 211//1 212//1 213//1
|
||||
f 211//1 213//1 208//1
|
||||
f 211//1 208//1 214//1
|
||||
f 214//1 208//1 207//1
|
||||
f 215//1 50//1 49//1
|
||||
f 215//1 49//1 178//1
|
||||
f 215//1 178//1 216//1
|
||||
f 216//1 178//1 177//1
|
||||
f 216//1 177//1 213//1
|
||||
f 216//1 213//1 212//1
|
||||
@@ -0,0 +1,25 @@
|
||||
[remap]
|
||||
|
||||
importer="wavefront_obj"
|
||||
importer_version=1
|
||||
type="Mesh"
|
||||
uid="uid://edg5thwvmf7x"
|
||||
path="res://.godot/imported/Honey.obj-eebe278f2aa3fe38d3546f10aa3ef814.mesh"
|
||||
|
||||
[deps]
|
||||
|
||||
files=["res://.godot/imported/Honey.obj-eebe278f2aa3fe38d3546f10aa3ef814.mesh"]
|
||||
|
||||
source_file="res://sky_demo/assets/meshes/Honey.obj"
|
||||
dest_files=["res://.godot/imported/Honey.obj-eebe278f2aa3fe38d3546f10aa3ef814.mesh", "res://.godot/imported/Honey.obj-eebe278f2aa3fe38d3546f10aa3ef814.mesh"]
|
||||
|
||||
[params]
|
||||
|
||||
generate_tangents=true
|
||||
generate_lods=true
|
||||
generate_shadow_mesh=true
|
||||
generate_lightmap_uv2=false
|
||||
generate_lightmap_uv2_texel_size=0.2
|
||||
scale_mesh=Vector3(1, 1, 1)
|
||||
offset_mesh=Vector3(0, 0, 0)
|
||||
force_disable_mesh_compression=false
|
||||
@@ -0,0 +1,25 @@
|
||||
[remap]
|
||||
|
||||
importer="wavefront_obj"
|
||||
importer_version=1
|
||||
type="Mesh"
|
||||
uid="uid://cr4asb0j76cfa"
|
||||
path="res://.godot/imported/KnotTorus.obj-2815ea52adbe9df4054d6b5c899a0d05.mesh"
|
||||
|
||||
[deps]
|
||||
|
||||
files=["res://.godot/imported/KnotTorus.obj-2815ea52adbe9df4054d6b5c899a0d05.mesh"]
|
||||
|
||||
source_file="res://sky_demo/assets/meshes/KnotTorus.obj"
|
||||
dest_files=["res://.godot/imported/KnotTorus.obj-2815ea52adbe9df4054d6b5c899a0d05.mesh", "res://.godot/imported/KnotTorus.obj-2815ea52adbe9df4054d6b5c899a0d05.mesh"]
|
||||
|
||||
[params]
|
||||
|
||||
generate_tangents=true
|
||||
generate_lods=true
|
||||
generate_shadow_mesh=true
|
||||
generate_lightmap_uv2=false
|
||||
generate_lightmap_uv2_texel_size=0.2
|
||||
scale_mesh=Vector3(1, 1, 1)
|
||||
offset_mesh=Vector3(0, 0, 0)
|
||||
force_disable_mesh_compression=false
|
||||
@@ -0,0 +1,610 @@
|
||||
# Blender v2.90.0 OBJ File: ''
|
||||
# www.blender.org
|
||||
mtllib RoundCube.mtl
|
||||
o Roundcube.001
|
||||
v 0.915470 -0.915470 -0.915470
|
||||
v 0.857735 -0.935401 -0.935401
|
||||
v 0.867700 -0.867700 -0.975594
|
||||
v 0.935401 -0.857735 -0.935401
|
||||
v 0.800000 -0.941421 -0.941421
|
||||
v 0.800000 -0.870711 -0.987083
|
||||
v -0.800000 -0.941421 -0.941421
|
||||
v -0.800000 -0.870711 -0.987083
|
||||
v -0.857735 -0.935401 -0.935401
|
||||
v -0.867700 -0.867700 -0.975594
|
||||
v -0.915470 -0.915470 -0.915470
|
||||
v -0.935401 -0.857735 -0.935401
|
||||
v 0.870711 -0.800000 -0.987083
|
||||
v 0.941421 -0.800000 -0.941421
|
||||
v 0.800000 -0.800000 -1.000000
|
||||
v -0.800000 -0.800000 -1.000000
|
||||
v -0.870711 -0.800000 -0.987083
|
||||
v -0.941421 -0.800000 -0.941421
|
||||
v 0.870711 0.800000 -0.987083
|
||||
v 0.941421 0.800000 -0.941421
|
||||
v 0.800000 0.800000 -1.000000
|
||||
v -0.800000 0.800000 -1.000000
|
||||
v -0.870711 0.800000 -0.987083
|
||||
v -0.941421 0.800000 -0.941421
|
||||
v 0.867700 0.867700 -0.975594
|
||||
v 0.935401 0.857735 -0.935401
|
||||
v 0.800000 0.870711 -0.987083
|
||||
v -0.800000 0.870711 -0.987083
|
||||
v -0.867700 0.867700 -0.975594
|
||||
v -0.935401 0.857735 -0.935401
|
||||
v 0.857735 0.935401 -0.935401
|
||||
v 0.915470 0.915470 -0.915470
|
||||
v 0.800000 0.941421 -0.941421
|
||||
v -0.800000 0.941421 -0.941421
|
||||
v -0.857735 0.935401 -0.935401
|
||||
v -0.915470 0.915470 -0.915470
|
||||
v -0.935401 -0.935401 -0.857735
|
||||
v -0.975594 -0.867700 -0.867700
|
||||
v -0.941421 -0.941421 -0.800000
|
||||
v -0.987083 -0.870711 -0.800000
|
||||
v -0.941421 -0.941421 0.800000
|
||||
v -0.987083 -0.870711 0.800000
|
||||
v -0.935401 -0.935401 0.857735
|
||||
v -0.975594 -0.867700 0.867700
|
||||
v -0.915470 -0.915470 0.915470
|
||||
v -0.935401 -0.857735 0.935401
|
||||
v -0.987083 -0.800000 -0.870711
|
||||
v -1.000000 -0.800000 -0.800000
|
||||
v -1.000000 -0.800000 0.800000
|
||||
v -0.987083 -0.800000 0.870711
|
||||
v -0.941421 -0.800000 0.941421
|
||||
v -0.987083 0.800000 -0.870711
|
||||
v -1.000000 0.800000 -0.800000
|
||||
v -1.000000 0.800000 0.800000
|
||||
v -0.987083 0.800000 0.870711
|
||||
v -0.941421 0.800000 0.941421
|
||||
v -0.975594 0.867700 -0.867700
|
||||
v -0.987083 0.870711 -0.800000
|
||||
v -0.987083 0.870711 0.800000
|
||||
v -0.975594 0.867700 0.867700
|
||||
v -0.935401 0.857735 0.935401
|
||||
v -0.935401 0.935401 -0.857735
|
||||
v -0.941421 0.941421 -0.800000
|
||||
v -0.941421 0.941421 0.800000
|
||||
v -0.935401 0.935401 0.857735
|
||||
v -0.915470 0.915470 0.915470
|
||||
v -0.857735 -0.935401 0.935401
|
||||
v -0.867700 -0.867700 0.975594
|
||||
v -0.800000 -0.941421 0.941421
|
||||
v -0.800000 -0.870711 0.987083
|
||||
v 0.800000 -0.941421 0.941421
|
||||
v 0.800000 -0.870711 0.987083
|
||||
v 0.857735 -0.935401 0.935401
|
||||
v 0.867700 -0.867700 0.975594
|
||||
v 0.915470 -0.915470 0.915470
|
||||
v 0.935401 -0.857735 0.935401
|
||||
v -0.870711 -0.800000 0.987083
|
||||
v -0.800000 -0.800000 1.000000
|
||||
v 0.800000 -0.800000 1.000000
|
||||
v 0.870711 -0.800000 0.987083
|
||||
v 0.941421 -0.800000 0.941421
|
||||
v -0.870711 0.800000 0.987083
|
||||
v -0.800000 0.800000 1.000000
|
||||
v 0.800000 0.800000 1.000000
|
||||
v 0.870711 0.800000 0.987083
|
||||
v 0.941421 0.800000 0.941421
|
||||
v -0.867700 0.867700 0.975594
|
||||
v -0.800000 0.870711 0.987083
|
||||
v 0.800000 0.870711 0.987083
|
||||
v 0.867700 0.867700 0.975594
|
||||
v 0.935401 0.857735 0.935401
|
||||
v -0.857735 0.935401 0.935401
|
||||
v -0.800000 0.941421 0.941421
|
||||
v 0.800000 0.941421 0.941421
|
||||
v 0.857735 0.935401 0.935401
|
||||
v 0.915470 0.915470 0.915470
|
||||
v 0.935401 -0.935401 0.857735
|
||||
v 0.975594 -0.867700 0.867700
|
||||
v 0.941421 -0.941421 0.800000
|
||||
v 0.987083 -0.870711 0.800000
|
||||
v 0.941421 -0.941421 -0.800000
|
||||
v 0.987083 -0.870711 -0.800000
|
||||
v 0.935401 -0.935401 -0.857735
|
||||
v 0.975594 -0.867700 -0.867700
|
||||
v 0.987083 -0.800000 0.870711
|
||||
v 1.000000 -0.800000 0.800000
|
||||
v 1.000000 -0.800000 -0.800000
|
||||
v 0.987083 -0.800000 -0.870711
|
||||
v 0.987083 0.800000 0.870711
|
||||
v 1.000000 0.800000 0.800000
|
||||
v 1.000000 0.800000 -0.800000
|
||||
v 0.987083 0.800000 -0.870711
|
||||
v 0.975594 0.867700 0.867700
|
||||
v 0.987083 0.870711 0.800000
|
||||
v 0.987083 0.870711 -0.800000
|
||||
v 0.975594 0.867700 -0.867700
|
||||
v 0.935401 0.935401 0.857735
|
||||
v 0.941421 0.941421 0.800000
|
||||
v 0.941421 0.941421 -0.800000
|
||||
v 0.935401 0.935401 -0.857735
|
||||
v 0.867700 -0.975594 0.867700
|
||||
v 0.800000 -0.987083 0.870711
|
||||
v -0.800000 -0.987083 0.870711
|
||||
v -0.867700 -0.975594 0.867700
|
||||
v 0.870711 -0.987083 0.800000
|
||||
v 0.800000 -1.000000 0.800000
|
||||
v -0.800000 -1.000000 0.800000
|
||||
v -0.870711 -0.987083 0.800000
|
||||
v 0.870711 -0.987083 -0.800000
|
||||
v 0.800000 -1.000000 -0.800000
|
||||
v -0.800000 -1.000000 -0.800000
|
||||
v -0.870711 -0.987083 -0.800000
|
||||
v 0.867700 -0.975594 -0.867700
|
||||
v 0.800000 -0.987083 -0.870711
|
||||
v -0.800000 -0.987083 -0.870711
|
||||
v -0.867700 -0.975594 -0.867700
|
||||
v 0.867700 0.975594 -0.867700
|
||||
v 0.800000 0.987083 -0.870711
|
||||
v -0.800000 0.987083 -0.870711
|
||||
v -0.867700 0.975594 -0.867700
|
||||
v 0.870711 0.987083 -0.800000
|
||||
v 0.800000 1.000000 -0.800000
|
||||
v -0.800000 1.000000 -0.800000
|
||||
v -0.870711 0.987083 -0.800000
|
||||
v 0.870711 0.987083 0.800000
|
||||
v 0.800000 1.000000 0.800000
|
||||
v -0.800000 1.000000 0.800000
|
||||
v -0.870711 0.987083 0.800000
|
||||
v 0.867700 0.975594 0.867700
|
||||
v 0.800000 0.987083 0.870711
|
||||
v -0.800000 0.987083 0.870711
|
||||
v -0.867700 0.975594 0.867700
|
||||
vn 0.5773 -0.5773 -0.5774
|
||||
vn 0.3042 -0.6732 -0.6739
|
||||
vn 0.3378 -0.3378 -0.8785
|
||||
vn 0.6732 -0.3042 -0.6739
|
||||
vn 0.0797 -0.7043 -0.7054
|
||||
vn 0.0846 -0.3617 -0.9284
|
||||
vn -0.0797 -0.7054 -0.7043
|
||||
vn -0.0826 -0.3629 -0.9282
|
||||
vn -0.3042 -0.6739 -0.6732
|
||||
vn -0.3398 -0.3398 -0.8770
|
||||
vn -0.5774 -0.5774 -0.5773
|
||||
vn -0.6739 -0.3042 -0.6732
|
||||
vn 0.3617 -0.0846 -0.9284
|
||||
vn 0.7043 -0.0797 -0.7054
|
||||
vn 0.0889 -0.0889 -0.9921
|
||||
vn -0.0861 -0.0861 -0.9926
|
||||
vn -0.3629 -0.0826 -0.9282
|
||||
vn -0.7054 -0.0797 -0.7043
|
||||
vn 0.3629 0.0826 -0.9282
|
||||
vn 0.7054 0.0797 -0.7043
|
||||
vn 0.0861 0.0861 -0.9926
|
||||
vn -0.0889 0.0889 -0.9921
|
||||
vn -0.3617 0.0846 -0.9284
|
||||
vn -0.7043 0.0797 -0.7054
|
||||
vn 0.3398 0.3398 -0.8770
|
||||
vn 0.6739 0.3042 -0.6732
|
||||
vn 0.0826 0.3629 -0.9282
|
||||
vn -0.0846 0.3617 -0.9284
|
||||
vn -0.3378 0.3378 -0.8785
|
||||
vn -0.6732 0.3042 -0.6739
|
||||
vn 0.3042 0.6739 -0.6732
|
||||
vn 0.5774 0.5774 -0.5773
|
||||
vn 0.0797 0.7054 -0.7043
|
||||
vn -0.0797 0.7043 -0.7054
|
||||
vn -0.3042 0.6732 -0.6739
|
||||
vn -0.5773 0.5773 -0.5774
|
||||
vn -0.6736 -0.6736 -0.3043
|
||||
vn -0.8785 -0.3378 -0.3378
|
||||
vn -0.7049 -0.7049 -0.0796
|
||||
vn -0.9284 -0.3617 -0.0847
|
||||
vn -0.7048 -0.7048 0.0814
|
||||
vn -0.9282 -0.3629 0.0826
|
||||
vn -0.6734 -0.6734 0.3053
|
||||
vn -0.8770 -0.3398 0.3398
|
||||
vn -0.5773 -0.5773 0.5774
|
||||
vn -0.6732 -0.3042 0.6739
|
||||
vn -0.9284 -0.0846 -0.3617
|
||||
vn -0.9921 -0.0889 -0.0889
|
||||
vn -0.9926 -0.0861 0.0861
|
||||
vn -0.9282 -0.0826 0.3629
|
||||
vn -0.7043 -0.0797 0.7054
|
||||
vn -0.9282 0.0826 -0.3629
|
||||
vn -0.9926 0.0861 -0.0861
|
||||
vn -0.9921 0.0889 0.0889
|
||||
vn -0.9284 0.0846 0.3618
|
||||
vn -0.7054 0.0797 0.7043
|
||||
vn -0.8770 0.3398 -0.3398
|
||||
vn -0.9282 0.3629 -0.0826
|
||||
vn -0.9284 0.3617 0.0846
|
||||
vn -0.8785 0.3378 0.3378
|
||||
vn -0.6739 0.3042 0.6732
|
||||
vn -0.6734 0.6734 -0.3053
|
||||
vn -0.7048 0.7048 -0.0814
|
||||
vn -0.7049 0.7049 0.0796
|
||||
vn -0.6736 0.6736 0.3043
|
||||
vn -0.5774 0.5774 0.5773
|
||||
vn -0.3042 -0.6732 0.6739
|
||||
vn -0.3378 -0.3378 0.8785
|
||||
vn -0.0797 -0.7043 0.7054
|
||||
vn -0.0846 -0.3617 0.9284
|
||||
vn 0.0797 -0.7054 0.7043
|
||||
vn 0.0826 -0.3629 0.9282
|
||||
vn 0.3042 -0.6739 0.6732
|
||||
vn 0.3398 -0.3398 0.8770
|
||||
vn 0.5774 -0.5774 0.5773
|
||||
vn 0.6739 -0.3042 0.6732
|
||||
vn -0.3617 -0.0846 0.9284
|
||||
vn -0.0889 -0.0889 0.9921
|
||||
vn 0.0861 -0.0861 0.9926
|
||||
vn 0.3629 -0.0826 0.9282
|
||||
vn 0.7054 -0.0797 0.7043
|
||||
vn -0.3629 0.0826 0.9282
|
||||
vn -0.0861 0.0861 0.9926
|
||||
vn 0.0889 0.0889 0.9921
|
||||
vn 0.3617 0.0846 0.9284
|
||||
vn 0.7043 0.0797 0.7054
|
||||
vn -0.3398 0.3398 0.8770
|
||||
vn -0.0826 0.3629 0.9282
|
||||
vn 0.0846 0.3617 0.9284
|
||||
vn 0.3378 0.3378 0.8785
|
||||
vn 0.6732 0.3042 0.6739
|
||||
vn -0.3042 0.6739 0.6732
|
||||
vn -0.0797 0.7054 0.7043
|
||||
vn 0.0797 0.7043 0.7054
|
||||
vn 0.3042 0.6732 0.6739
|
||||
vn 0.5773 0.5773 0.5774
|
||||
vn 0.6736 -0.6736 0.3043
|
||||
vn 0.8785 -0.3378 0.3378
|
||||
vn 0.7049 -0.7049 0.0796
|
||||
vn 0.9284 -0.3617 0.0847
|
||||
vn 0.7048 -0.7048 -0.0814
|
||||
vn 0.9282 -0.3629 -0.0826
|
||||
vn 0.6734 -0.6734 -0.3053
|
||||
vn 0.8770 -0.3398 -0.3398
|
||||
vn 0.9284 -0.0846 0.3617
|
||||
vn 0.9921 -0.0889 0.0889
|
||||
vn 0.9926 -0.0861 -0.0861
|
||||
vn 0.9282 -0.0826 -0.3629
|
||||
vn 0.9282 0.0826 0.3629
|
||||
vn 0.9926 0.0861 0.0861
|
||||
vn 0.9921 0.0889 -0.0889
|
||||
vn 0.9284 0.0846 -0.3618
|
||||
vn 0.8770 0.3398 0.3398
|
||||
vn 0.9282 0.3629 0.0826
|
||||
vn 0.9284 0.3617 -0.0846
|
||||
vn 0.8785 0.3378 -0.3378
|
||||
vn 0.6734 0.6734 0.3053
|
||||
vn 0.7048 0.7048 0.0814
|
||||
vn 0.7049 0.7049 -0.0796
|
||||
vn 0.6736 0.6736 -0.3043
|
||||
vn 0.3378 -0.8785 0.3378
|
||||
vn 0.0846 -0.9284 0.3617
|
||||
vn -0.0826 -0.9282 0.3629
|
||||
vn -0.3398 -0.8770 0.3398
|
||||
vn 0.3617 -0.9284 0.0846
|
||||
vn 0.0889 -0.9921 0.0889
|
||||
vn -0.0861 -0.9926 0.0861
|
||||
vn -0.3629 -0.9282 0.0826
|
||||
vn 0.3629 -0.9282 -0.0826
|
||||
vn 0.0861 -0.9926 -0.0861
|
||||
vn -0.0889 -0.9921 -0.0889
|
||||
vn -0.3617 -0.9284 -0.0846
|
||||
vn 0.3398 -0.8770 -0.3398
|
||||
vn 0.0827 -0.9281 -0.3629
|
||||
vn -0.0846 -0.9284 -0.3617
|
||||
vn -0.3378 -0.8785 -0.3378
|
||||
vn 0.3378 0.8785 -0.3378
|
||||
vn 0.0846 0.9284 -0.3617
|
||||
vn -0.0826 0.9282 -0.3629
|
||||
vn -0.3398 0.8770 -0.3398
|
||||
vn 0.3617 0.9284 -0.0846
|
||||
vn 0.0889 0.9921 -0.0889
|
||||
vn -0.0861 0.9926 -0.0861
|
||||
vn -0.3629 0.9282 -0.0826
|
||||
vn 0.3629 0.9282 0.0826
|
||||
vn 0.0861 0.9926 0.0861
|
||||
vn -0.0889 0.9921 0.0889
|
||||
vn -0.3617 0.9284 0.0846
|
||||
vn 0.3398 0.8770 0.3398
|
||||
vn 0.0827 0.9281 0.3629
|
||||
vn -0.0846 0.9284 0.3617
|
||||
vn -0.3378 0.8785 0.3378
|
||||
usemtl None
|
||||
s 1
|
||||
f 1//1 2//2 3//3
|
||||
f 1//1 3//3 4//4
|
||||
f 2//2 5//5 6//6
|
||||
f 2//2 6//6 3//3
|
||||
f 5//5 7//7 8//8
|
||||
f 5//5 8//8 6//6
|
||||
f 7//7 9//9 10//10
|
||||
f 7//7 10//10 8//8
|
||||
f 9//9 11//11 12//12
|
||||
f 9//9 12//12 10//10
|
||||
f 4//4 3//3 13//13
|
||||
f 4//4 13//13 14//14
|
||||
f 3//3 6//6 15//15
|
||||
f 3//3 15//15 13//13
|
||||
f 6//6 8//8 16//16
|
||||
f 6//6 16//16 15//15
|
||||
f 8//8 10//10 17//17
|
||||
f 8//8 17//17 16//16
|
||||
f 10//10 12//12 18//18
|
||||
f 10//10 18//18 17//17
|
||||
f 14//14 13//13 19//19
|
||||
f 14//14 19//19 20//20
|
||||
f 13//13 15//15 21//21
|
||||
f 13//13 21//21 19//19
|
||||
f 15//15 16//16 22//22
|
||||
f 15//15 22//22 21//21
|
||||
f 16//16 17//17 23//23
|
||||
f 16//16 23//23 22//22
|
||||
f 17//17 18//18 24//24
|
||||
f 17//17 24//24 23//23
|
||||
f 20//20 19//19 25//25
|
||||
f 20//20 25//25 26//26
|
||||
f 19//19 21//21 27//27
|
||||
f 19//19 27//27 25//25
|
||||
f 21//21 22//22 28//28
|
||||
f 21//21 28//28 27//27
|
||||
f 22//22 23//23 29//29
|
||||
f 22//22 29//29 28//28
|
||||
f 23//23 24//24 30//30
|
||||
f 23//23 30//30 29//29
|
||||
f 26//26 25//25 31//31
|
||||
f 26//26 31//31 32//32
|
||||
f 25//25 27//27 33//33
|
||||
f 25//25 33//33 31//31
|
||||
f 27//27 28//28 34//34
|
||||
f 27//27 34//34 33//33
|
||||
f 28//28 29//29 35//35
|
||||
f 28//28 35//35 34//34
|
||||
f 29//29 30//30 36//36
|
||||
f 29//29 36//36 35//35
|
||||
f 11//11 37//37 38//38
|
||||
f 11//11 38//38 12//12
|
||||
f 37//37 39//39 40//40
|
||||
f 37//37 40//40 38//38
|
||||
f 39//39 41//41 42//42
|
||||
f 39//39 42//42 40//40
|
||||
f 41//41 43//43 44//44
|
||||
f 41//41 44//44 42//42
|
||||
f 43//43 45//45 46//46
|
||||
f 43//43 46//46 44//44
|
||||
f 12//12 38//38 47//47
|
||||
f 12//12 47//47 18//18
|
||||
f 38//38 40//40 48//48
|
||||
f 38//38 48//48 47//47
|
||||
f 40//40 42//42 49//49
|
||||
f 40//40 49//49 48//48
|
||||
f 42//42 44//44 50//50
|
||||
f 42//42 50//50 49//49
|
||||
f 44//44 46//46 51//51
|
||||
f 44//44 51//51 50//50
|
||||
f 18//18 47//47 52//52
|
||||
f 18//18 52//52 24//24
|
||||
f 47//47 48//48 53//53
|
||||
f 47//47 53//53 52//52
|
||||
f 48//48 49//49 54//54
|
||||
f 48//48 54//54 53//53
|
||||
f 49//49 50//50 55//55
|
||||
f 49//49 55//55 54//54
|
||||
f 50//50 51//51 56//56
|
||||
f 50//50 56//56 55//55
|
||||
f 24//24 52//52 57//57
|
||||
f 24//24 57//57 30//30
|
||||
f 52//52 53//53 58//58
|
||||
f 52//52 58//58 57//57
|
||||
f 53//53 54//54 59//59
|
||||
f 53//53 59//59 58//58
|
||||
f 54//54 55//55 60//60
|
||||
f 54//54 60//60 59//59
|
||||
f 55//55 56//56 61//61
|
||||
f 55//55 61//61 60//60
|
||||
f 30//30 57//57 62//62
|
||||
f 30//30 62//62 36//36
|
||||
f 57//57 58//58 63//63
|
||||
f 57//57 63//63 62//62
|
||||
f 58//58 59//59 64//64
|
||||
f 58//58 64//64 63//63
|
||||
f 59//59 60//60 65//65
|
||||
f 59//59 65//65 64//64
|
||||
f 60//60 61//61 66//66
|
||||
f 60//60 66//66 65//65
|
||||
f 45//45 67//67 68//68
|
||||
f 45//45 68//68 46//46
|
||||
f 67//67 69//69 70//70
|
||||
f 67//67 70//70 68//68
|
||||
f 69//69 71//71 72//72
|
||||
f 69//69 72//72 70//70
|
||||
f 71//71 73//73 74//74
|
||||
f 71//71 74//74 72//72
|
||||
f 73//73 75//75 76//76
|
||||
f 73//73 76//76 74//74
|
||||
f 46//46 68//68 77//77
|
||||
f 46//46 77//77 51//51
|
||||
f 68//68 70//70 78//78
|
||||
f 68//68 78//78 77//77
|
||||
f 70//70 72//72 79//79
|
||||
f 70//70 79//79 78//78
|
||||
f 72//72 74//74 80//80
|
||||
f 72//72 80//80 79//79
|
||||
f 74//74 76//76 81//81
|
||||
f 74//74 81//81 80//80
|
||||
f 51//51 77//77 82//82
|
||||
f 51//51 82//82 56//56
|
||||
f 77//77 78//78 83//83
|
||||
f 77//77 83//83 82//82
|
||||
f 78//78 79//79 84//84
|
||||
f 78//78 84//84 83//83
|
||||
f 79//79 80//80 85//85
|
||||
f 79//79 85//85 84//84
|
||||
f 80//80 81//81 86//86
|
||||
f 80//80 86//86 85//85
|
||||
f 56//56 82//82 87//87
|
||||
f 56//56 87//87 61//61
|
||||
f 82//82 83//83 88//88
|
||||
f 82//82 88//88 87//87
|
||||
f 83//83 84//84 89//89
|
||||
f 83//83 89//89 88//88
|
||||
f 84//84 85//85 90//90
|
||||
f 84//84 90//90 89//89
|
||||
f 85//85 86//86 91//91
|
||||
f 85//85 91//91 90//90
|
||||
f 61//61 87//87 92//92
|
||||
f 61//61 92//92 66//66
|
||||
f 87//87 88//88 93//93
|
||||
f 87//87 93//93 92//92
|
||||
f 88//88 89//89 94//94
|
||||
f 88//88 94//94 93//93
|
||||
f 89//89 90//90 95//95
|
||||
f 89//89 95//95 94//94
|
||||
f 90//90 91//91 96//96
|
||||
f 90//90 96//96 95//95
|
||||
f 75//75 97//97 98//98
|
||||
f 75//75 98//98 76//76
|
||||
f 97//97 99//99 100//100
|
||||
f 97//97 100//100 98//98
|
||||
f 99//99 101//101 102//102
|
||||
f 99//99 102//102 100//100
|
||||
f 101//101 103//103 104//104
|
||||
f 101//101 104//104 102//102
|
||||
f 103//103 1//1 4//4
|
||||
f 103//103 4//4 104//104
|
||||
f 76//76 98//98 105//105
|
||||
f 76//76 105//105 81//81
|
||||
f 98//98 100//100 106//106
|
||||
f 98//98 106//106 105//105
|
||||
f 100//100 102//102 107//107
|
||||
f 100//100 107//107 106//106
|
||||
f 102//102 104//104 108//108
|
||||
f 102//102 108//108 107//107
|
||||
f 104//104 4//4 14//14
|
||||
f 104//104 14//14 108//108
|
||||
f 81//81 105//105 109//109
|
||||
f 81//81 109//109 86//86
|
||||
f 105//105 106//106 110//110
|
||||
f 105//105 110//110 109//109
|
||||
f 106//106 107//107 111//111
|
||||
f 106//106 111//111 110//110
|
||||
f 107//107 108//108 112//112
|
||||
f 107//107 112//112 111//111
|
||||
f 108//108 14//14 20//20
|
||||
f 108//108 20//20 112//112
|
||||
f 86//86 109//109 113//113
|
||||
f 86//86 113//113 91//91
|
||||
f 109//109 110//110 114//114
|
||||
f 109//109 114//114 113//113
|
||||
f 110//110 111//111 115//115
|
||||
f 110//110 115//115 114//114
|
||||
f 111//111 112//112 116//116
|
||||
f 111//111 116//116 115//115
|
||||
f 112//112 20//20 26//26
|
||||
f 112//112 26//26 116//116
|
||||
f 91//91 113//113 117//117
|
||||
f 91//91 117//117 96//96
|
||||
f 113//113 114//114 118//118
|
||||
f 113//113 118//118 117//117
|
||||
f 114//114 115//115 119//119
|
||||
f 114//114 119//119 118//118
|
||||
f 115//115 116//116 120//120
|
||||
f 115//115 120//120 119//119
|
||||
f 116//116 26//26 32//32
|
||||
f 116//116 32//32 120//120
|
||||
f 75//75 73//73 121//121
|
||||
f 75//75 121//121 97//97
|
||||
f 73//73 71//71 122//122
|
||||
f 73//73 122//122 121//121
|
||||
f 71//71 69//69 123//123
|
||||
f 71//71 123//123 122//122
|
||||
f 69//69 67//67 124//124
|
||||
f 69//69 124//124 123//123
|
||||
f 67//67 45//45 43//43
|
||||
f 67//67 43//43 124//124
|
||||
f 97//97 121//121 125//125
|
||||
f 97//97 125//125 99//99
|
||||
f 121//121 122//122 126//126
|
||||
f 121//121 126//126 125//125
|
||||
f 122//122 123//123 127//127
|
||||
f 122//122 127//127 126//126
|
||||
f 123//123 124//124 128//128
|
||||
f 123//123 128//128 127//127
|
||||
f 124//124 43//43 41//41
|
||||
f 124//124 41//41 128//128
|
||||
f 99//99 125//125 129//129
|
||||
f 99//99 129//129 101//101
|
||||
f 125//125 126//126 130//130
|
||||
f 125//125 130//130 129//129
|
||||
f 126//126 127//127 131//131
|
||||
f 126//126 131//131 130//130
|
||||
f 127//127 128//128 132//132
|
||||
f 127//127 132//132 131//131
|
||||
f 128//128 41//41 39//39
|
||||
f 128//128 39//39 132//132
|
||||
f 101//101 129//129 133//133
|
||||
f 101//101 133//133 103//103
|
||||
f 129//129 130//130 134//134
|
||||
f 129//129 134//134 133//133
|
||||
f 130//130 131//131 135//135
|
||||
f 130//130 135//135 134//134
|
||||
f 131//131 132//132 136//136
|
||||
f 131//131 136//136 135//135
|
||||
f 132//132 39//39 37//37
|
||||
f 132//132 37//37 136//136
|
||||
f 103//103 133//133 2//2
|
||||
f 103//103 2//2 1//1
|
||||
f 133//133 134//134 5//5
|
||||
f 133//133 5//5 2//2
|
||||
f 134//134 135//135 7//7
|
||||
f 134//134 7//7 5//5
|
||||
f 135//135 136//136 9//9
|
||||
f 135//135 9//9 7//7
|
||||
f 136//136 37//37 11//11
|
||||
f 136//136 11//11 9//9
|
||||
f 32//32 31//31 137//137
|
||||
f 32//32 137//137 120//120
|
||||
f 31//31 33//33 138//138
|
||||
f 31//31 138//138 137//137
|
||||
f 33//33 34//34 139//139
|
||||
f 33//33 139//139 138//138
|
||||
f 34//34 35//35 140//140
|
||||
f 34//34 140//140 139//139
|
||||
f 35//35 36//36 62//62
|
||||
f 35//35 62//62 140//140
|
||||
f 120//120 137//137 141//141
|
||||
f 120//120 141//141 119//119
|
||||
f 137//137 138//138 142//142
|
||||
f 137//137 142//142 141//141
|
||||
f 138//138 139//139 143//143
|
||||
f 138//138 143//143 142//142
|
||||
f 139//139 140//140 144//144
|
||||
f 139//139 144//144 143//143
|
||||
f 140//140 62//62 63//63
|
||||
f 140//140 63//63 144//144
|
||||
f 119//119 141//141 145//145
|
||||
f 119//119 145//145 118//118
|
||||
f 141//141 142//142 146//146
|
||||
f 141//141 146//146 145//145
|
||||
f 142//142 143//143 147//147
|
||||
f 142//142 147//147 146//146
|
||||
f 143//143 144//144 148//148
|
||||
f 143//143 148//148 147//147
|
||||
f 144//144 63//63 64//64
|
||||
f 144//144 64//64 148//148
|
||||
f 118//118 145//145 149//149
|
||||
f 118//118 149//149 117//117
|
||||
f 145//145 146//146 150//150
|
||||
f 145//145 150//150 149//149
|
||||
f 146//146 147//147 151//151
|
||||
f 146//146 151//151 150//150
|
||||
f 147//147 148//148 152//152
|
||||
f 147//147 152//152 151//151
|
||||
f 148//148 64//64 65//65
|
||||
f 148//148 65//65 152//152
|
||||
f 117//117 149//149 95//95
|
||||
f 117//117 95//95 96//96
|
||||
f 149//149 150//150 94//94
|
||||
f 149//149 94//94 95//95
|
||||
f 150//150 151//151 93//93
|
||||
f 150//150 93//93 94//94
|
||||
f 151//151 152//152 92//92
|
||||
f 151//151 92//92 93//93
|
||||
f 152//152 65//65 66//66
|
||||
f 152//152 66//66 92//92
|
||||
@@ -0,0 +1,25 @@
|
||||
[remap]
|
||||
|
||||
importer="wavefront_obj"
|
||||
importer_version=1
|
||||
type="Mesh"
|
||||
uid="uid://dfje04v6keeiq"
|
||||
path="res://.godot/imported/RoundCube.obj-8d7ac4d74239ed92bc142c8b01f2646f.mesh"
|
||||
|
||||
[deps]
|
||||
|
||||
files=["res://.godot/imported/RoundCube.obj-8d7ac4d74239ed92bc142c8b01f2646f.mesh"]
|
||||
|
||||
source_file="res://sky_demo/assets/meshes/RoundCube.obj"
|
||||
dest_files=["res://.godot/imported/RoundCube.obj-8d7ac4d74239ed92bc142c8b01f2646f.mesh", "res://.godot/imported/RoundCube.obj-8d7ac4d74239ed92bc142c8b01f2646f.mesh"]
|
||||
|
||||
[params]
|
||||
|
||||
generate_tangents=true
|
||||
generate_lods=true
|
||||
generate_shadow_mesh=true
|
||||
generate_lightmap_uv2=false
|
||||
generate_lightmap_uv2_texel_size=0.2
|
||||
scale_mesh=Vector3(1, 1, 1)
|
||||
offset_mesh=Vector3(0, 0, 0)
|
||||
force_disable_mesh_compression=false
|
||||
@@ -0,0 +1,25 @@
|
||||
[remap]
|
||||
|
||||
importer="wavefront_obj"
|
||||
importer_version=1
|
||||
type="Mesh"
|
||||
uid="uid://cv4gbcuvj16hw"
|
||||
path="res://.godot/imported/Suzanne.obj-0c1ba5f63dc9f10e4c987c54185ffe28.mesh"
|
||||
|
||||
[deps]
|
||||
|
||||
files=["res://.godot/imported/Suzanne.obj-0c1ba5f63dc9f10e4c987c54185ffe28.mesh"]
|
||||
|
||||
source_file="res://sky_demo/assets/meshes/Suzanne.obj"
|
||||
dest_files=["res://.godot/imported/Suzanne.obj-0c1ba5f63dc9f10e4c987c54185ffe28.mesh", "res://.godot/imported/Suzanne.obj-0c1ba5f63dc9f10e4c987c54185ffe28.mesh"]
|
||||
|
||||
[params]
|
||||
|
||||
generate_tangents=true
|
||||
generate_lods=true
|
||||
generate_shadow_mesh=true
|
||||
generate_lightmap_uv2=false
|
||||
generate_lightmap_uv2_texel_size=0.2
|
||||
scale_mesh=Vector3(1, 1, 1)
|
||||
offset_mesh=Vector3(0, 0, 0)
|
||||
force_disable_mesh_compression=false
|
||||
@@ -0,0 +1,25 @@
|
||||
[remap]
|
||||
|
||||
importer="wavefront_obj"
|
||||
importer_version=1
|
||||
type="Mesh"
|
||||
uid="uid://d137osfsks6c"
|
||||
path="res://.godot/imported/Teapot.obj-924c553112080416c403cd33beb71dfc.mesh"
|
||||
|
||||
[deps]
|
||||
|
||||
files=["res://.godot/imported/Teapot.obj-924c553112080416c403cd33beb71dfc.mesh"]
|
||||
|
||||
source_file="res://sky_demo/assets/meshes/Teapot.obj"
|
||||
dest_files=["res://.godot/imported/Teapot.obj-924c553112080416c403cd33beb71dfc.mesh", "res://.godot/imported/Teapot.obj-924c553112080416c403cd33beb71dfc.mesh"]
|
||||
|
||||
[params]
|
||||
|
||||
generate_tangents=true
|
||||
generate_lods=true
|
||||
generate_shadow_mesh=true
|
||||
generate_lightmap_uv2=false
|
||||
generate_lightmap_uv2_texel_size=0.2
|
||||
scale_mesh=Vector3(0.5, 0.5, 0.5)
|
||||
offset_mesh=Vector3(0, 0, 0)
|
||||
force_disable_mesh_compression=false
|
||||
@@ -0,0 +1,25 @@
|
||||
[remap]
|
||||
|
||||
importer="wavefront_obj"
|
||||
importer_version=1
|
||||
type="Mesh"
|
||||
uid="uid://fgjxevq0rxeg"
|
||||
path="res://.godot/imported/Torus.obj-fc9b08c54ed8ceda61d4f61c16947eee.mesh"
|
||||
|
||||
[deps]
|
||||
|
||||
files=["res://.godot/imported/Torus.obj-fc9b08c54ed8ceda61d4f61c16947eee.mesh"]
|
||||
|
||||
source_file="res://sky_demo/assets/meshes/Torus.obj"
|
||||
dest_files=["res://.godot/imported/Torus.obj-fc9b08c54ed8ceda61d4f61c16947eee.mesh", "res://.godot/imported/Torus.obj-fc9b08c54ed8ceda61d4f61c16947eee.mesh"]
|
||||
|
||||
[params]
|
||||
|
||||
generate_tangents=true
|
||||
generate_lods=true
|
||||
generate_shadow_mesh=true
|
||||
generate_lightmap_uv2=false
|
||||
generate_lightmap_uv2_texel_size=0.2
|
||||
scale_mesh=Vector3(1, 1, 1)
|
||||
offset_mesh=Vector3(0, 0, 0)
|
||||
force_disable_mesh_compression=false
|
||||
@@ -0,0 +1,11 @@
|
||||
# Blender v2.90.0 OBJ File: ''
|
||||
# www.blender.org
|
||||
mtllib Triangle.mtl
|
||||
o Triangle_Triangle.001
|
||||
v 0.000024 0.000000 -0.576993
|
||||
v 0.500024 0.000000 0.289033
|
||||
v -0.499976 0.000000 0.289033
|
||||
vn 0.0000 -1.0000 0.0000
|
||||
usemtl None
|
||||
s 1
|
||||
f 1//1 2//1 3//1
|
||||
@@ -0,0 +1,25 @@
|
||||
[remap]
|
||||
|
||||
importer="wavefront_obj"
|
||||
importer_version=1
|
||||
type="Mesh"
|
||||
uid="uid://dlye2mrl4eaf0"
|
||||
path="res://.godot/imported/Triangle.obj-3c138a828e7782242ccb4890cc3b2d19.mesh"
|
||||
|
||||
[deps]
|
||||
|
||||
files=["res://.godot/imported/Triangle.obj-3c138a828e7782242ccb4890cc3b2d19.mesh"]
|
||||
|
||||
source_file="res://sky_demo/assets/meshes/Triangle.obj"
|
||||
dest_files=["res://.godot/imported/Triangle.obj-3c138a828e7782242ccb4890cc3b2d19.mesh", "res://.godot/imported/Triangle.obj-3c138a828e7782242ccb4890cc3b2d19.mesh"]
|
||||
|
||||
[params]
|
||||
|
||||
generate_tangents=true
|
||||
generate_lods=true
|
||||
generate_shadow_mesh=true
|
||||
generate_lightmap_uv2=false
|
||||
generate_lightmap_uv2_texel_size=0.2
|
||||
scale_mesh=Vector3(1, 1, 1)
|
||||
offset_mesh=Vector3(0, 0, 0)
|
||||
force_disable_mesh_compression=false
|
||||
@@ -0,0 +1,25 @@
|
||||
[remap]
|
||||
|
||||
importer="wavefront_obj"
|
||||
importer_version=1
|
||||
type="Mesh"
|
||||
uid="uid://c7ewwo01t1axy"
|
||||
path="res://.godot/imported/TwistedTorus.obj-8356ad27153e1a0fb4d2fdf2513945d4.mesh"
|
||||
|
||||
[deps]
|
||||
|
||||
files=["res://.godot/imported/TwistedTorus.obj-8356ad27153e1a0fb4d2fdf2513945d4.mesh"]
|
||||
|
||||
source_file="res://sky_demo/assets/meshes/TwistedTorus.obj"
|
||||
dest_files=["res://.godot/imported/TwistedTorus.obj-8356ad27153e1a0fb4d2fdf2513945d4.mesh", "res://.godot/imported/TwistedTorus.obj-8356ad27153e1a0fb4d2fdf2513945d4.mesh"]
|
||||
|
||||
[params]
|
||||
|
||||
generate_tangents=true
|
||||
generate_lods=true
|
||||
generate_shadow_mesh=true
|
||||
generate_lightmap_uv2=false
|
||||
generate_lightmap_uv2_texel_size=0.2
|
||||
scale_mesh=Vector3(1, 1, 1)
|
||||
offset_mesh=Vector3(0, 0, 0)
|
||||
force_disable_mesh_compression=false
|
||||
@@ -0,0 +1,68 @@
|
||||
[gd_scene load_steps=10 format=3 uid="uid://bjmikbn4ac2lu"]
|
||||
|
||||
[sub_resource type="PlaneMesh" id="1"]
|
||||
size = Vector2(50, 50)
|
||||
|
||||
[sub_resource type="BoxMesh" id="2"]
|
||||
size = Vector3(10, 25, 10)
|
||||
|
||||
[sub_resource type="BoxMesh" id="3"]
|
||||
size = Vector3(10, 50, 10)
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="4"]
|
||||
|
||||
[sub_resource type="MultiMesh" id="MultiMesh_b8jmf"]
|
||||
transform_format = 1
|
||||
instance_count = 64
|
||||
mesh = SubResource("3")
|
||||
buffer = PackedFloat32Array(0, 0, -1.001, 151.655, 0, 1.001, 0, 0, 1.001, 0, 0, -12.0823, -1.001, 0, 0, -26.5305, 0, 1.001, 0, 0, 0, 0, -1.001, -179.117, 0, 0, -1.001, -25.9358, 0, 1.001, 0, 0, 1.001, 0, 0, 203.634, 0, 0, -1.001, 138.885, 0, 1.001, 0, 0, 1.001, 0, 0, 128.126, -1.001, 0, 0, 78.8868, 0, 1.001, 0, 0, 0, 0, -1.001, -188.999, -1.001, 0, 0, -235.705, 0, 1.001, 0, 0, 0, 0, -1.001, 6.51101, 0, 0, -1.001, -36.4516, 0, 1.001, 0, 0, 1.001, 0, 0, 86.5434, 0, 0, -1.001, 181.803, 0, 1.001, 0, 0, 1.001, 0, 0, 219.977, -1.001, 0, 0, -87.5359, 0, 1.001, 0, 0, 0, 0, -1.001, 55.8013, -1.001, 0, 0, 94.3313, 0, 1.001, 0, 0, 0, 0, -1.001, -190.418, 0, 0, -1.001, 125.304, 0, 1.001, 0, 0, 1.001, 0, 0, 1.31473, 0, 0, -1.001, -224.692, 0, 1.001, 0, 0, 1.001, 0, 0, 236.823, -1.001, 0, 0, -173.451, 0, 1.001, 0, 0, 0, 0, -1.001, 11.2965, 0, 0, -1.001, 48.5798, 0, 1.001, 0, 0, 1.001, 0, 0, 42.5393, -1.001, 0, 0, -183.574, 0, 1.001, 0, 0, 0, 0, -1.001, -212.186, -1.001, 0, 0, -90.4111, 0, 1.001, 0, 0, 0, 0, -1.001, -45.7838, 0, 0, -1.001, -119.093, 0, 1.001, 0, 0, 1.001, 0, 0, 205.069, 0, 0, -1.001, 231.323, 0, 1.001, 0, 0, 1.001, 0, 0, 178.499, -1.001, 0, 0, 63.5853, 0, 1.001, 0, 0, 0, 0, -1.001, -133.931, -1.001, 0, 0, 189.997, 0, 1.001, 0, 0, 0, 0, -1.001, -204.345, -1.001, 0, 0, -185.958, 0, 1.001, 0, 0, 0, 0, -1.001, -83.7796, -1.001, 0, 0, 121.066, 0, 1.001, 0, 0, 0, 0, -1.001, -142.239, 0, 0, -1.001, 197.274, 0, 1.001, 0, 0, 1.001, 0, 0, -65.8683, 0, 0, -1.001, -100.613, 0, 1.001, 0, 0, 1.001, 0, 0, 103.569, -1.001, 0, 0, -56.8292, 0, 1.001, 0, 0, 0, 0, -1.001, -13.6392, -1.001, 0, 0, -126.982, 0, 1.001, 0, 0, 0, 0, -1.001, 19.286, -1.001, 0, 0, -165.299, 0, 1.001, 0, 0, 0, 0, -1.001, -239.327, 0, 0, -1.001, 51.9921, 0, 1.001, 0, 0, 1.001, 0, 0, 163.609, 0, 0, -1.001, 119.734, 0, 1.001, 0, 0, 1.001, 0, 0, -60.7672, -1.001, 0, 0, -229.119, 0, 1.001, 0, 0, 0, 0, -1.001, 60.4606, 0, 0, -1.001, 157.69, 0, 1.001, 0, 0, 1.001, 0, 0, 49.408, -1.001, 0, 0, 120.299, 0, 1.001, 0, 0, 0, 0, -1.001, -175.736, -1.001, 0, 0, -102.876, 0, 1.001, 0, 0, 0, 0, -1.001, -119.353, 0, 0, -1.001, -56.4621, 0, 1.001, 0, 0, 1.001, 0, 0, 78.5369, -1.001, 0, 0, -215.666, 0, 1.001, 0, 0, 0, 0, -1.001, 91.2701, 0, 0, -1.001, -78.4358, 0, 1.001, 0, 0, 1.001, 0, 0, 193.726, -1.001, 0, 0, 43.7711, 0, 1.001, 0, 0, 0, 0, -1.001, -165.066, -1.001, 0, 0, 131.296, 0, 1.001, 0, 0, 0, 0, -1.001, -246.084, -1.001, 0, 0, 111.286, 0, 1.001, 0, 0, 0, 0, -1.001, -135.847, 0, 0, -1.001, 161.335, 0, 1.001, 0, 0, 1.001, 0, 0, 109.029, -1.001, 0, 0, 148.471, 0, 1.001, 0, 0, 0, 0, -1.001, -197.788, -1.001, 0, 0, -158.214, 0, 1.001, 0, 0, 0, 0, -1.001, -173.545, -1.001, 0, 0, 54.8234, 0, 1.001, 0, 0, 0, 0, -1.001, -166.621, 0, 0, -1.001, 39.9216, 0, 1.001, 0, 0, 1.001, 0, 0, 83.7301, -1.001, 0, 0, -10.3294, 0, 1.001, 0, 0, 0, 0, -1.001, -183.779, 0, 0, -1.001, 20.1091, 0, 1.001, 0, 0, 1.001, 0, 0, 190.198, -1.001, 0, 0, 11.3488, 0, 1.001, 0, 0, 0, 0, -1.001, -25.3071, 0, 0, -1.001, 3.67114, 0, 1.001, 0, 0, 1.001, 0, 0, 188.979, -1.001, 0, 0, -133.638, 0, 1.001, 0, 0, 0, 0, -1.001, -191.612, -1.001, 0, 0, -162.968, 0, 1.001, 0, 0, 0, 0, -1.001, -180.099, -1.001, 0, 0, 104.224, 0, 1.001, 0, 0, 0, 0, -1.001, -107.805, -1.001, 0, 0, 92.7499, 0, 1.001, 0, 0, 0, 0, -1.001, -101.027, 0, 0, -1.001, 219.68, 0, 1.001, 0, 0, 1.001, 0, 0, -204.336, 0, 0, -1.001, 171.15, 0, 1.001, 0, 0, 1.001, 0, 0, -52.7658, 0, 0, -1.001, 162.602, 0, 1.001, 0, 0, 1.001, 0, 0, -72.4319, 0, 0, -1.001, 92.4865, 0, 1.001, 0, 0, 1.001, 0, 0, 217.463, 0, 0, -1.001, 129.862, 0, 1.001, 0, 0, 1.001, 0, 0, 45.8048, -1.001, 0, 0, -245.405, 0, 1.001, 0, 0, 0, 0, -1.001, 116.336, -1.001, 0, 0, -235.774, 0, 1.001, 0, 0, 0, 0, -1.001, -124.031, -1.001, 0, 0, -86.5609, 0, 1.001, 0, 0, 0, 0, -1.001, -67.951, 0, 0, -1.001, 25.5948, 0, 1.001, 0, 0, 1.001, 0, 0, 45.2402, -1.001, 0, 0, -80.1298, 0, 1.001, 0, 0, 0, 0, -1.001, 26.1805, 0, 0, -1.001, 157.982, 0, 1.001, 0, 0, 1.001, 0, 0, -142.473, -1.001, 0, 0, -170.094, 0, 1.001, 0, 0, 0, 0, -1.001, 98.9675)
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="6"]
|
||||
|
||||
[sub_resource type="MultiMesh" id="MultiMesh_yc2ag"]
|
||||
transform_format = 1
|
||||
instance_count = 15
|
||||
mesh = SubResource("3")
|
||||
buffer = PackedFloat32Array(0, 0, -1.001, -149.843, 0, 1.001, 0, 0, 1.001, 0, 0, 192.542, -1.001, 0, 0, 38.8109, 0, 1.001, 0, 0, 0, 0, -1.001, -246.475, 0, 0, -1.001, 125.821, 0, 1.001, 0, 0, 1.001, 0, 0, 88.4455, 0, 0, -1.001, 220.682, 0, 1.001, 0, 0, 1.001, 0, 0, 230.371, 0, 0, -1.001, 234.017, 0, 1.001, 0, 0, 1.001, 0, 0, 218.471, -1.001, 0, 0, -205.498, 0, 1.001, 0, 0, 0, 0, -1.001, -136.485, -1.001, 0, 0, 136.08, 0, 1.001, 0, 0, 0, 0, -1.001, -148.189, 0, 0, -1.001, 174.855, 0, 1.001, 0, 0, 1.001, 0, 0, 235.047, 0, 0, -1.001, 212.885, 0, 1.001, 0, 0, 1.001, 0, 0, -201.404, -1.001, 0, 0, -111.263, 0, 1.001, 0, 0, 0, 0, -1.001, 43.1221, 0, 0, -1.001, -139.411, 0, 1.001, 0, 0, 1.001, 0, 0, 220.659, -1.001, 0, 0, -18.8148, 0, 1.001, 0, 0, 0, 0, -1.001, -180.02, 0, 0, -1.001, 234.238, 0, 1.001, 0, 0, 1.001, 0, 0, 94.7901, 0, 0, -1.001, 176.954, 0, 1.001, 0, 0, 1.001, 0, 0, 210.195, -1.001, 0, 0, -119.643, 0, 1.001, 0, 0, 0, 0, -1.001, -58.247)
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="8"]
|
||||
metallic = 0.44
|
||||
roughness = 0.58
|
||||
|
||||
[sub_resource type="MultiMesh" id="MultiMesh_wdqw1"]
|
||||
transform_format = 1
|
||||
instance_count = 15
|
||||
mesh = SubResource("2")
|
||||
buffer = PackedFloat32Array(-1.001, 0, 0, -154.088, 0, 1.001, 0, 0, 0, 0, -1.001, -10.0072, 0, 0, -1.001, 0.577511, 0, 1.001, 0, 0, 1.001, 0, 0, 247.545, -1.001, 0, 0, -248.461, 0, 1.001, 0, 0, 0, 0, -1.001, 218.481, -1.001, 0, 0, -218.555, 0, 1.001, 0, 0, 0, 0, -1.001, -94.9203, 0, 0, -1.001, 41.3343, 0, 1.001, 0, 0, 1.001, 0, 0, 17.2596, -1.001, 0, 0, -71.8356, 0, 1.001, 0, 0, 0, 0, -1.001, 71.2163, 0, 0, -1.001, -150.315, 0, 1.001, 0, 0, 1.001, 0, 0, 188.47, 0, 0, -1.001, 137.882, 0, 1.001, 0, 0, 1.001, 0, 0, 210.962, 0, 0, -1.001, 88.4959, 0, 1.001, 0, 0, 1.001, 0, 0, 249.89, 0, 0, -1.001, 202.684, 0, 1.001, 0, 0, 1.001, 0, 0, 147.871, -1.001, 0, 0, 84.5897, 0, 1.001, 0, 0, 0, 0, -1.001, -120.238, 0, 0, -1.001, 92.0758, 0, 1.001, 0, 0, 1.001, 0, 0, 186.997, 0, 0, -1.001, 157.212, 0, 1.001, 0, 0, 1.001, 0, 0, 105.553, 0, 0, -1.001, 198.619, 0, 1.001, 0, 0, 1.001, 0, 0, 47.1543, -1.001, 0, 0, -211.812, 0, 1.001, 0, 0, 0, 0, -1.001, -157.802)
|
||||
|
||||
[node name="Node3D" type="Node3D"]
|
||||
|
||||
[node name="Floor" type="MeshInstance3D" parent="."]
|
||||
transform = Transform3D(10, 0, 0, 0, 1, 0, 0, 0, 10, 0, 0, 0)
|
||||
visible = false
|
||||
mesh = SubResource("1")
|
||||
|
||||
[node name="Building" type="MeshInstance3D" parent="."]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 12.7351, 0)
|
||||
mesh = SubResource("2")
|
||||
|
||||
[node name="Build2" type="MeshInstance3D" parent="."]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 68.1965, 22.3025, 0)
|
||||
mesh = SubResource("3")
|
||||
|
||||
[node name="MultiMeshInstance3D" type="MultiMeshInstance3D" parent="."]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 25, 0)
|
||||
material_override = SubResource("4")
|
||||
multimesh = SubResource("MultiMesh_b8jmf")
|
||||
|
||||
[node name="MultiMeshInstance2" type="MultiMeshInstance3D" parent="."]
|
||||
transform = Transform3D(1, 0, 0, 0, 3, 0, 0, 0, 1, 0, 75, 0)
|
||||
visible = false
|
||||
material_override = SubResource("6")
|
||||
multimesh = SubResource("MultiMesh_yc2ag")
|
||||
|
||||
[node name="MultiMeshInstance3" type="MultiMeshInstance3D" parent="."]
|
||||
transform = Transform3D(1, 0, 0, 0, 2, 0, 0, 0, 1, 0, 25, 0)
|
||||
visible = false
|
||||
material_override = SubResource("8")
|
||||
multimesh = SubResource("MultiMesh_wdqw1")
|
||||
@@ -0,0 +1,206 @@
|
||||
[gd_scene load_steps=22 format=3 uid="uid://dipn6t27p0nap"]
|
||||
|
||||
[ext_resource type="PackedScene" uid="uid://bjmikbn4ac2lu" path="res://demo/assets/scenes/BuildingBlocks.tscn" id="1"]
|
||||
[ext_resource type="PackedScene" uid="uid://1ev4o7q8l4ey" path="res://demo/assets/meshes/Compass.tscn" id="2_gjkdl"]
|
||||
[ext_resource type="Texture2D" uid="uid://hl8w41k0t8ia" path="res://demo/assets/textures/ProtGrid0.jpg" id="8"]
|
||||
[ext_resource type="ArrayMesh" uid="uid://d137osfsks6c" path="res://demo/assets/meshes/Teapot.obj" id="9"]
|
||||
[ext_resource type="ArrayMesh" uid="uid://cr4asb0j76cfa" path="res://demo/assets/meshes/KnotTorus.obj" id="10"]
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="1"]
|
||||
albedo_color = Color(0.396078, 0.396078, 0.396078, 1)
|
||||
albedo_texture = ExtResource("8")
|
||||
uv1_scale = Vector3(150, 150, 1)
|
||||
texture_filter = 5
|
||||
|
||||
[sub_resource type="PlaneMesh" id="2"]
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="3"]
|
||||
metallic = 0.75
|
||||
roughness = 0.4
|
||||
|
||||
[sub_resource type="SphereMesh" id="4"]
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="5"]
|
||||
metallic = 1.0
|
||||
roughness = 0.01
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="6"]
|
||||
roughness = 0.56
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="7"]
|
||||
transparency = 1
|
||||
albedo_color = Color(1, 1, 1, 0.105882)
|
||||
metallic = 1.0
|
||||
roughness = 0.1
|
||||
refraction_enabled = true
|
||||
refraction_scale = 0.15
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="8"]
|
||||
transparency = 1
|
||||
albedo_color = Color(0.984314, 0.94902, 0.94902, 0.815686)
|
||||
metallic = 1.0
|
||||
roughness = 0.29
|
||||
refraction_enabled = true
|
||||
refraction_scale = 0.39
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="9"]
|
||||
transparency = 1
|
||||
albedo_color = Color(1, 1, 1, 0.564706)
|
||||
metallic = 0.16
|
||||
roughness = 0.07
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_bvo0c"]
|
||||
albedo_color = Color(0.831, 0.686, 0.215, 1)
|
||||
metallic = 1.0
|
||||
roughness = 0.06
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_tloy4"]
|
||||
albedo_color = Color(0.960784, 0.960784, 0.960784, 1)
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="12"]
|
||||
albedo_color = Color(0.960784, 0.960784, 0.960784, 1)
|
||||
metallic = 0.1
|
||||
roughness = 0.1
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="13"]
|
||||
specular_mode = 4
|
||||
albedo_color = Color(0, 0, 0, 1)
|
||||
emission_enabled = true
|
||||
emission = Color(0.972549, 0.505882, 0.054902, 1)
|
||||
emission_energy_multiplier = 5.0
|
||||
|
||||
[sub_resource type="CylinderMesh" id="14"]
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="15"]
|
||||
specular_mode = 4
|
||||
albedo_color = Color(0, 0, 0, 1)
|
||||
emission_enabled = true
|
||||
emission = Color(0.054902, 0.8, 0.972549, 1)
|
||||
emission_energy_multiplier = 5.0
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="16"]
|
||||
specular_mode = 4
|
||||
albedo_color = Color(0, 0, 0, 1)
|
||||
emission_enabled = true
|
||||
emission = Color(0.054902, 0.564706, 0.972549, 1)
|
||||
emission_energy_multiplier = 5.0
|
||||
|
||||
[node name="PrimitivesCity" type="Node3D"]
|
||||
|
||||
[node name="Floor" type="MeshInstance3D" parent="."]
|
||||
transform = Transform3D(2000, 0, 0, 0, 1, 0, 0, 0, 2000, 0, 0, 0)
|
||||
layers = 4
|
||||
material_override = SubResource("1")
|
||||
mesh = SubResource("2")
|
||||
|
||||
[node name="Compass" parent="." instance=ExtResource("2_gjkdl")]
|
||||
|
||||
[node name="Props" type="Node3D" parent="."]
|
||||
|
||||
[node name="Sphere0" type="MeshInstance3D" parent="Props"]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 8, 1, 4)
|
||||
material_override = SubResource("3")
|
||||
mesh = SubResource("4")
|
||||
skeleton = NodePath("../..")
|
||||
|
||||
[node name="Sphere1" type="MeshInstance3D" parent="Props"]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 8, 1, 2)
|
||||
material_override = SubResource("5")
|
||||
mesh = SubResource("4")
|
||||
skeleton = NodePath("../..")
|
||||
|
||||
[node name="Sphere2" type="MeshInstance3D" parent="Props"]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 8, 1, 0)
|
||||
material_override = SubResource("6")
|
||||
mesh = SubResource("4")
|
||||
skeleton = NodePath("../..")
|
||||
|
||||
[node name="Teapot" type="MeshInstance3D" parent="Props"]
|
||||
transform = Transform3D(0.707107, 0, -0.707107, 0, 1, 0, 0.707107, 0, 0.707107, 8, 0, 6.5)
|
||||
material_override = SubResource("7")
|
||||
mesh = ExtResource("9")
|
||||
skeleton = NodePath("../..")
|
||||
|
||||
[node name="Teapot2" type="MeshInstance3D" parent="Props"]
|
||||
transform = Transform3D(0.707107, 0, -0.707107, 0, 1, 0, 0.707107, 0, 0.707107, 8, 0, 10)
|
||||
material_override = SubResource("8")
|
||||
mesh = ExtResource("9")
|
||||
skeleton = NodePath("../..")
|
||||
|
||||
[node name="Teapot3" type="MeshInstance3D" parent="Props"]
|
||||
transform = Transform3D(0.707107, 0, -0.707107, 0, 1, 0, 0.707107, 0, 0.707107, 7.96891, 9.53674e-07, 13.998)
|
||||
material_override = SubResource("9")
|
||||
mesh = ExtResource("9")
|
||||
skeleton = NodePath("../..")
|
||||
|
||||
[node name="KnotTorusGold" type="MeshInstance3D" parent="Props"]
|
||||
transform = Transform3D(0.5, 0, 0, 0, 0.5, 0, 0, 0, 0.5, 8, 1, -3.5)
|
||||
material_override = SubResource("StandardMaterial3D_bvo0c")
|
||||
mesh = ExtResource("10")
|
||||
skeleton = NodePath("../..")
|
||||
|
||||
[node name="KnotTorusMatte" type="MeshInstance3D" parent="Props"]
|
||||
transform = Transform3D(0.5, 0, 0, 0, 0.5, 0, 0, 0, 0.5, 8, 1, -8)
|
||||
material_override = SubResource("StandardMaterial3D_tloy4")
|
||||
mesh = ExtResource("10")
|
||||
skeleton = NodePath("../..")
|
||||
|
||||
[node name="KnotTorusGloss" type="MeshInstance3D" parent="Props"]
|
||||
transform = Transform3D(0.5, 0, 0, 0, 0.5, 0, 0, 0, 0.5, 8, 1, -13)
|
||||
material_override = SubResource("12")
|
||||
mesh = ExtResource("10")
|
||||
skeleton = NodePath("../..")
|
||||
|
||||
[node name="LCylinder" type="MeshInstance3D" parent="Props"]
|
||||
transform = Transform3D(0.5, 0, 0, 0, 1, 0, 0, 0, 0.5, 0, 1, 13.25)
|
||||
material_override = SubResource("13")
|
||||
mesh = SubResource("14")
|
||||
skeleton = NodePath("../..")
|
||||
|
||||
[node name="LCylinder2" type="MeshInstance3D" parent="Props"]
|
||||
transform = Transform3D(0.5, 0, 0, 0, 1, 0, 0, 0, 0.5, -6, 1, 6)
|
||||
material_override = SubResource("15")
|
||||
mesh = SubResource("14")
|
||||
skeleton = NodePath("../..")
|
||||
|
||||
[node name="LCylinder3" type="MeshInstance3D" parent="Props"]
|
||||
transform = Transform3D(0.5, 0, 0, 0, 1, 0, 0, 0, 0.5, -6, 1, -6)
|
||||
material_override = SubResource("16")
|
||||
mesh = SubResource("14")
|
||||
skeleton = NodePath("../..")
|
||||
|
||||
[node name="OmniLight3D2" type="OmniLight3D" parent="Props/LCylinder3"]
|
||||
transform = Transform3D(2, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0)
|
||||
light_color = Color(0.054902, 0.564706, 0.972549, 1)
|
||||
shadow_enabled = true
|
||||
omni_range = 20.0
|
||||
|
||||
[node name="LCylinder4" type="MeshInstance3D" parent="Props"]
|
||||
transform = Transform3D(0.5, 0, 0, 0, 1, 0, 0, 0, 0.5, 0, 1, -13.25)
|
||||
material_override = SubResource("13")
|
||||
mesh = SubResource("14")
|
||||
skeleton = NodePath("../..")
|
||||
|
||||
[node name="OmniLight3D" type="OmniLight3D" parent="Props/LCylinder4"]
|
||||
transform = Transform3D(2, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0)
|
||||
light_color = Color(0.972549, 0.505882, 0.054902, 1)
|
||||
shadow_enabled = true
|
||||
omni_range = 20.0
|
||||
|
||||
[node name="Buildings" parent="." instance=ExtResource("1")]
|
||||
transform = Transform3D(-5.05739e-08, 0, 1, 0, 1.799, 0, -1.157, 0, -4.37114e-08, -318, 0, -598.815)
|
||||
|
||||
[node name="Buildings2" parent="." instance=ExtResource("1")]
|
||||
transform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, -317.435, 0, 7.93411)
|
||||
|
||||
[node name="Buildings3" parent="." instance=ExtResource("1")]
|
||||
transform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, -316.869, 0, 601.186)
|
||||
|
||||
[node name="Buildings4" parent="." instance=ExtResource("1")]
|
||||
transform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, 342, 0, -599.437)
|
||||
|
||||
[node name="Buildings5" parent="." instance=ExtResource("1")]
|
||||
transform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, 342.565, 0, 15.5)
|
||||
|
||||
[node name="Buildings6" parent="." instance=ExtResource("1")]
|
||||
transform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, 266.673, 0, 603.016)
|
||||
|
After Width: | Height: | Size: 4.3 KiB |
@@ -0,0 +1,41 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://hl8w41k0t8ia"
|
||||
path.s3tc="res://.godot/imported/ProtGrid0.jpg-1d1abe9518ac3b20293622b66313fc20.s3tc.ctex"
|
||||
metadata={
|
||||
"imported_formats": ["s3tc_bptc"],
|
||||
"vram_texture": true
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://sky_demo/assets/textures/ProtGrid0.jpg"
|
||||
dest_files=["res://.godot/imported/ProtGrid0.jpg-1d1abe9518ac3b20293622b66313fc20.s3tc.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=2
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=2
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=true
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=0
|
||||
|
After Width: | Height: | Size: 5.3 KiB |
@@ -0,0 +1,41 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://b2fgjtbqrhy3b"
|
||||
path.s3tc="res://.godot/imported/ProtGrid1.jpg-5eed5a394d6251965c0778f01d8decf0.s3tc.ctex"
|
||||
metadata={
|
||||
"imported_formats": ["s3tc_bptc"],
|
||||
"vram_texture": true
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://sky_demo/assets/textures/ProtGrid1.jpg"
|
||||
dest_files=["res://.godot/imported/ProtGrid1.jpg-5eed5a394d6251965c0778f01d8decf0.s3tc.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=2
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=2
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=false
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
@@ -1,224 +0,0 @@
|
||||
@tool
|
||||
extends McpTestSuite
|
||||
## 回归测试:抱起物品系统的契约。
|
||||
##
|
||||
## 谁能被抱由**物品自己**决定(挂 carryable.gd + 进 carryable 组),玩家只管找/抱/扔;
|
||||
## 抱持偏移和投掷速度也写在物品上。联机只同步 `carrier_peer` 一个整数 ——
|
||||
## 抱着的时候位置不走同步,每端都从持有者的抱持点算出来,这几条契约坏了联机就会「各看各的」。
|
||||
|
||||
const PLAYER_SCENE_PATH := "res://character/player/player.tscn"
|
||||
const ITEM_SCENE_PATH := "res://world/items/carryable_box.tscn"
|
||||
const CARRY_ACTION := "carry"
|
||||
const THROW_ACTION := "throw_carry"
|
||||
const HOLDER_PEER := 7
|
||||
|
||||
|
||||
func suite_name() -> String:
|
||||
return "carry"
|
||||
|
||||
|
||||
## 编辑器进程里的 InputMap 只有内建动作,项目动作要手动 load 一次才在
|
||||
func _ensure_input_map() -> void:
|
||||
InputMap.load_from_project_settings()
|
||||
|
||||
|
||||
## 把节点塞进场景树,让 _ready() 真的跑起来;track() 负责回收
|
||||
func _into_tree(node: Node) -> Node:
|
||||
var editor := Engine.get_singleton("EditorInterface")
|
||||
var root: Node = null
|
||||
if editor != null:
|
||||
root = editor.get_edited_scene_root()
|
||||
if root == null:
|
||||
root = Engine.get_main_loop().root
|
||||
root.add_child(node)
|
||||
track(node)
|
||||
return node
|
||||
|
||||
|
||||
func _spawn_item() -> Carryable:
|
||||
var packed := load(ITEM_SCENE_PATH) as PackedScene
|
||||
if packed == null:
|
||||
return null
|
||||
return _into_tree(packed.instantiate()) as Carryable
|
||||
|
||||
|
||||
## 造一个「假持有者」:玩家节点在世界里以 peer id 命名、进 net_player 组,抱持点挂在相机架上
|
||||
func _fake_holder() -> Node3D:
|
||||
var holder := Node3D.new()
|
||||
holder.name = str(HOLDER_PEER)
|
||||
holder.add_to_group(Carryable.PLAYER_GROUP)
|
||||
var rig := Node3D.new()
|
||||
rig.name = "CameraRig"
|
||||
holder.add_child(rig)
|
||||
var socket := Marker3D.new()
|
||||
socket.name = "CarrySocket"
|
||||
socket.position = Vector3(0.0, 1.0, -2.0)
|
||||
rig.add_child(socket)
|
||||
return _into_tree(holder) as Node3D
|
||||
|
||||
|
||||
func test_carry_actions_exist() -> void:
|
||||
_ensure_input_map()
|
||||
# 动作没了按键就完全没反应,而且不报错 —— 这种静默失效必须由测试兜住
|
||||
assert_true(InputMap.has_action(CARRY_ACTION), "缺少 %s 动作,左键抱不起来" % CARRY_ACTION)
|
||||
assert_true(InputMap.has_action(THROW_ACTION), "缺少 %s 动作,长按 Q 扔不出去" % THROW_ACTION)
|
||||
|
||||
|
||||
func test_player_scene_has_carry_nodes() -> void:
|
||||
var packed := load(PLAYER_SCENE_PATH) as PackedScene
|
||||
assert_true(packed != null, "%s 未能加载" % PLAYER_SCENE_PATH)
|
||||
if packed == null:
|
||||
return
|
||||
var player := packed.instantiate()
|
||||
track(player)
|
||||
var socket := player.get_node_or_null("CameraRig/CarrySocket")
|
||||
assert_true(socket is Marker3D, "CameraRig 下缺少 CarrySocket(Marker3D)—— 物品没有抱持点")
|
||||
var arms := player.get_node_or_null("Body/CarryArms")
|
||||
assert_true(arms != null, "Body 下缺少 CarryArms 节点")
|
||||
if arms != null:
|
||||
assert_true(arms.get_script() != null and str(arms.get_script().resource_path).ends_with("carry_arms.gd"),
|
||||
"Body/CarryArms 没挂 carry_arms.gd,胳膊不会抱东西")
|
||||
|
||||
|
||||
## 第一人称要看得见自己的胳膊。
|
||||
## player.gd 不是 @tool,在编辑器里是 placeholder、方法调不动,所以这里查两件事:
|
||||
## 代码里确实按 `arm_` 前缀把胳膊留下来,且模型里真的有这些独立 mesh(换模型改名会红)。
|
||||
func test_first_person_keeps_arms_visible() -> void:
|
||||
var file := FileAccess.open("res://character/player/player.gd", FileAccess.READ)
|
||||
assert_true(file != null, "读不到 player.gd")
|
||||
if file == null:
|
||||
return
|
||||
var source := file.get_as_text()
|
||||
assert_true(source.contains("_hide_body_keep_arms"), "player.gd 里没有「第一人称保留胳膊」的逻辑")
|
||||
assert_true(source.contains("begins_with(\"arm_\")"),
|
||||
"藏身体时没有按 arm_ 前缀把胳膊留下来,第一人称会看不到自己的手")
|
||||
var packed := load(PLAYER_SCENE_PATH) as PackedScene
|
||||
if packed == null:
|
||||
return
|
||||
var player := packed.instantiate()
|
||||
track(player)
|
||||
var skeleton := player.get_node_or_null("Body/mouse/MouseRig/Skeleton3D")
|
||||
assert_true(skeleton != null, "找不到骨架,模型结构变了?")
|
||||
if skeleton == null:
|
||||
return
|
||||
var arms := 0
|
||||
var others := 0
|
||||
for child in skeleton.get_children():
|
||||
var mesh := child as MeshInstance3D
|
||||
if mesh == null:
|
||||
continue
|
||||
if str(mesh.name).begins_with("arm_"):
|
||||
arms += 1
|
||||
else:
|
||||
others += 1
|
||||
assert_true(arms > 0, "模型里没有 arm_* 网格,第一人称看不到手")
|
||||
assert_true(others > 0, "模型里没有别的部位网格,藏身体就无从谈起")
|
||||
|
||||
|
||||
func test_example_item_is_carryable() -> void:
|
||||
var item := _spawn_item()
|
||||
assert_true(item != null, "%s 实例化失败 —— 可抱物品的示例没了" % ITEM_SCENE_PATH)
|
||||
if item == null:
|
||||
return
|
||||
assert_true(item.is_in_group(Carryable.GROUP),
|
||||
"可抱物品没进 %s 组,玩家就找不到它" % Carryable.GROUP)
|
||||
assert_true(item.get_node_or_null("Collision") is CollisionShape3D,
|
||||
"示例物品没有碰撞体,扔出去会穿地")
|
||||
|
||||
|
||||
## 联机靠 Sync 复制 position / rotation / carrier_peer;漏了 carrier_peer 别人就不知道被抱走了。
|
||||
func test_item_sync_config_covers_carry_state() -> void:
|
||||
var item := _spawn_item()
|
||||
if item == null:
|
||||
return
|
||||
var sync := item.get_node_or_null("Sync") as MultiplayerSynchronizer
|
||||
assert_true(sync != null, "示例物品没有 MultiplayerSynchronizer,联机看不到别人抱走它")
|
||||
if sync == null or sync.replication_config == null:
|
||||
return
|
||||
var paths: Array[String] = []
|
||||
for path in sync.replication_config.get_properties():
|
||||
paths.append(str(path))
|
||||
for required in [".:carrier_peer", ".:position", ".:rotation"]:
|
||||
assert_true(paths.has(required), "同步配置里少了 %s(实际 %s)" % [required, paths])
|
||||
|
||||
|
||||
func test_carry_freezes_body_and_restores_on_release() -> void:
|
||||
var item := _spawn_item()
|
||||
if item == null:
|
||||
return
|
||||
var layer := item.collision_layer
|
||||
assert_true(item.can_be_carried(), "新物品应该是可以抱的")
|
||||
item.request_pickup(HOLDER_PEER)
|
||||
assert_eq(item.carrier_peer, HOLDER_PEER, "抱起请求没记到 carrier_peer 上")
|
||||
assert_true(not item.can_be_carried(), "抱起来之后不该还能被别人抱走")
|
||||
assert_true(item.freeze, "抱起来要冻结物理,否则会和抱着它的人打架")
|
||||
assert_eq(item.collision_layer, 0, "抱起来要清掉碰撞层,否则物品会把玩家顶飞")
|
||||
item.request_release(Vector3(1.0, 2.0, 3.0))
|
||||
assert_eq(item.carrier_peer, 0, "放下之后 carrier_peer 要清零")
|
||||
assert_true(item.can_be_carried(), "放下之后应该又能被抱")
|
||||
assert_true(not item.freeze, "单机 / 服务器端放下要恢复物理模拟")
|
||||
assert_eq(item.collision_layer, layer, "放下要恢复原来的碰撞层")
|
||||
assert_true(item.linear_velocity.is_equal_approx(Vector3(1.0, 2.0, 3.0)),
|
||||
"放下时要继承扔出去的速度,实际 %s" % item.linear_velocity)
|
||||
|
||||
|
||||
## 抱着的时候位置不走同步:每端都从持有者的抱持点算。这里用一个假持有者验这件事。
|
||||
func test_carried_item_follows_holder_socket() -> void:
|
||||
var item := _spawn_item()
|
||||
var holder := _fake_holder()
|
||||
if item == null or holder == null:
|
||||
return
|
||||
item.request_pickup(HOLDER_PEER)
|
||||
item._physics_process(0.016)
|
||||
var socket := holder.get_node("CameraRig/CarrySocket") as Node3D
|
||||
var expected := socket.global_position + socket.global_basis * item.hold_offset
|
||||
assert_true(item.global_position.distance_to(expected) < 0.001,
|
||||
"物品没跟住持有者的抱持点:实际 %s,期望 %s" % [item.global_position, expected])
|
||||
# 松开之后就不该再跟了
|
||||
item.request_release(Vector3.ZERO)
|
||||
var after_release := item.global_position
|
||||
holder.position += Vector3(10.0, 0.0, 0.0)
|
||||
item._physics_process(0.016)
|
||||
assert_true(item.global_position.distance_to(after_release) < 0.001,
|
||||
"已经放手了还在跟持有者跑")
|
||||
|
||||
|
||||
func test_throw_speed_scales_with_charge() -> void:
|
||||
var item := _spawn_item()
|
||||
if item == null:
|
||||
return
|
||||
assert_true(absf(item.throw_speed(0.0) - item.throw_speed_min) < 0.001,
|
||||
"蓄力为 0 时应该是最低出手速度")
|
||||
assert_true(absf(item.throw_speed(1.0) - item.throw_speed_max) < 0.001,
|
||||
"蓄满时应该是最高出手速度")
|
||||
assert_true(item.throw_speed(0.5) > item.throw_speed_min and item.throw_speed(0.5) < item.throw_speed_max,
|
||||
"出手速度应该随蓄力单调变化")
|
||||
assert_true(item.throw_speed(9.0) <= item.throw_speed_max, "蓄力超出范围要被夹住")
|
||||
|
||||
|
||||
## 抱起姿势必须真的把胳膊伸到身体**前方**(模型正面是本地 +Z,尾巴在 -Z 那一侧)。
|
||||
## 参数写反或者骨骼名写错时,这里会红 —— 光看代码看不出胳膊朝哪边伸。
|
||||
func test_carry_pose_reaches_forward() -> void:
|
||||
var packed := load(PLAYER_SCENE_PATH) as PackedScene
|
||||
if packed == null:
|
||||
return
|
||||
var player := _into_tree(packed.instantiate())
|
||||
var arms := player.get_node_or_null("Body/CarryArms") as CarryArms
|
||||
var skeleton := player.get_node_or_null("Body/mouse/MouseRig/Skeleton3D") as Skeleton3D
|
||||
assert_true(arms != null and skeleton != null, "玩家场景里缺 CarryArms 或骨架")
|
||||
if arms == null or skeleton == null:
|
||||
return
|
||||
var hand := skeleton.find_bone("arm_l_03")
|
||||
assert_true(hand >= 0, "骨架里没有 arm_l_03,胳膊姿势会不完整")
|
||||
if hand < 0:
|
||||
return
|
||||
var before := skeleton.get_bone_global_pose(hand).origin
|
||||
arms.set_carrying(true, 0.0)
|
||||
# 直接推进两次让它混合到位(测试里没有引擎帧)
|
||||
arms._process(1.0)
|
||||
arms._process(1.0)
|
||||
var after := skeleton.get_bone_global_pose(hand).origin
|
||||
assert_true(after.z > before.z + 0.5,
|
||||
"抱起来时手要伸到身体前方:抱之前 z=%.2f,抱之后 z=%.2f" % [before.z, after.z])
|
||||
assert_true(after.y > before.y,
|
||||
"抱起来时手要抬到身前的高度:抱之前 y=%.2f,抱之后 y=%.2f" % [before.y, after.y])
|
||||
@@ -1 +0,0 @@
|
||||
uid://bcvmvwwjphdf5
|
||||
@@ -1,251 +0,0 @@
|
||||
@tool
|
||||
extends McpTestSuite
|
||||
## 回归测试:脸部那套「贴花 + 预设 + 画笔」的契约。
|
||||
##
|
||||
## body_mesh 只有一个材质 mat_body,脸是 shader 里贴在身体正面上的一块**贴花**:
|
||||
## 用模型本地坐标 (x, y) 定位、用法线朝前的程度当遮罩,所以和 UV 孤岛怎么排无关,
|
||||
## 也不会因为越界糊到肚子/背上。这里盯住:贴花参数还在、矩形大致落在模型包围盒里
|
||||
## (模型换尺度或换原点时会红)、以及 FaceRig 不许再抢着传这些 uniform。
|
||||
|
||||
const PLAYER_SCENE_PATH := "res://character/player/player.tscn"
|
||||
const SHADER_PATH := "res://character/player/face/face_paint.gdshader"
|
||||
const PRESET_DIR := "res://character/player/face/presets"
|
||||
const BODY_MESH_PATH := "Body/mouse/MouseRig/Skeleton3D/body_mesh"
|
||||
const BODY_MATERIAL_NAME := "mat_body"
|
||||
## 模型本地坐标,给点容差
|
||||
const EPS := 0.05
|
||||
|
||||
|
||||
func suite_name() -> String:
|
||||
return "face"
|
||||
|
||||
|
||||
func _shader_code() -> String:
|
||||
var file := FileAccess.open(SHADER_PATH, FileAccess.READ)
|
||||
assert_true(file != null, "%s 加载失败" % SHADER_PATH)
|
||||
if file == null:
|
||||
return ""
|
||||
return file.get_as_text()
|
||||
|
||||
|
||||
## 从 shader 源码里抓 `名字 = vec2(a, b);` 的两个数(uniform 和 const 都能抓)。
|
||||
func _shader_vec2(code: String, name: String) -> Vector2:
|
||||
var marker := "%s = vec2(" % name
|
||||
var start := code.find(marker)
|
||||
assert_true(start >= 0, "shader 里找不到 %s 的 vec2 默认值" % name)
|
||||
if start < 0:
|
||||
return Vector2(INF, INF)
|
||||
start += marker.length()
|
||||
var stop := code.find(")", start)
|
||||
if stop < 0:
|
||||
return Vector2(INF, INF)
|
||||
var parts := code.substr(start, stop - start).split(",")
|
||||
if parts.size() != 2:
|
||||
return Vector2(INF, INF)
|
||||
return Vector2(float(parts[0].strip_edges()), float(parts[1].strip_edges()))
|
||||
|
||||
|
||||
func _player() -> Node:
|
||||
var packed := load(PLAYER_SCENE_PATH) as PackedScene
|
||||
assert_true(packed != null, "%s 未能加载" % PLAYER_SCENE_PATH)
|
||||
if packed == null:
|
||||
return null
|
||||
var player := packed.instantiate()
|
||||
track(player)
|
||||
return player
|
||||
|
||||
|
||||
func _body_mesh() -> MeshInstance3D:
|
||||
var player := _player()
|
||||
if player == null:
|
||||
return null
|
||||
return player.get_node_or_null(BODY_MESH_PATH) as MeshInstance3D
|
||||
|
||||
|
||||
func test_player_has_face_rig_wired() -> void:
|
||||
var player := _player()
|
||||
if player == null:
|
||||
return
|
||||
var rig := player.get_node_or_null("FaceRig") as FaceRig
|
||||
assert_true(rig != null, "Player 下缺少 FaceRig 节点")
|
||||
if rig == null:
|
||||
return
|
||||
assert_eq(rig.surface_index, 0, "body_mesh 只剩一个材质,FaceRig 必须覆盖 surface 0")
|
||||
assert_true(rig.get_node_or_null(rig.mesh_path) is MeshInstance3D,
|
||||
"FaceRig.mesh_path(%s)解析不到 MeshInstance3D —— 换模型后路径要跟着改" % rig.mesh_path)
|
||||
# 贴花参数只写在 shader 里;脚本一旦也传这几个 uniform,就会把调好的值盖掉
|
||||
var script_text := FileAccess.open("res://character/player/face/face_rig.gd", FileAccess.READ).get_as_text()
|
||||
for name in ["face_center", "face_size", "face_flip_y", "face_front_edge"]:
|
||||
assert_true(not script_text.contains("set_shader_parameter(\"%s\"" % name),
|
||||
"face_rig.gd 又在传 %s 了 —— shader 顶部调好的值会被脚本覆盖" % name)
|
||||
|
||||
|
||||
func test_body_mesh_has_single_material() -> void:
|
||||
var mi := _body_mesh()
|
||||
assert_true(mi != null, "找不到 %s" % BODY_MESH_PATH)
|
||||
if mi == null:
|
||||
return
|
||||
var mesh := mi.mesh as ArrayMesh
|
||||
assert_true(mesh != null, "body_mesh 的 mesh 不是 ArrayMesh")
|
||||
if mesh == null:
|
||||
return
|
||||
assert_eq(mesh.get_surface_count(), 1, "body_mesh 应该只有一个材质槽了")
|
||||
var material := mesh.surface_get_material(0)
|
||||
assert_true(material != null, "surface 0 没有材质")
|
||||
if material == null:
|
||||
return
|
||||
assert_eq(material.resource_name, BODY_MATERIAL_NAME,
|
||||
"body_mesh 的材质不再是 %s,FaceRig 抄身体参数会失效" % BODY_MATERIAL_NAME)
|
||||
var standard := material as StandardMaterial3D
|
||||
assert_true(standard != null, "%s 不是 StandardMaterial3D" % BODY_MATERIAL_NAME)
|
||||
if standard != null:
|
||||
assert_true(standard.albedo_texture != null, "%s 没有 BaseColor 贴图,身体底色会变成白" % BODY_MATERIAL_NAME)
|
||||
|
||||
|
||||
## 贴花矩形是模型本地坐标,必须落在模型自己的包围盒里,否则表情会飘到体外。
|
||||
func test_face_decal_inside_body_bounds() -> void:
|
||||
var code := _shader_code()
|
||||
if code.is_empty():
|
||||
return
|
||||
var center := _shader_vec2(code, "face_center")
|
||||
var size := _shader_vec2(code, "face_size")
|
||||
if not center.is_finite() or not size.is_finite():
|
||||
return
|
||||
assert_true(size.x > 0.0 and size.y > 0.0, "face_size 必须是正数(要翻转请用 face_flip_y)")
|
||||
var mi := _body_mesh()
|
||||
if mi == null:
|
||||
return
|
||||
var bounds := mi.mesh.get_aabb()
|
||||
var rect_min := center - size * 0.5
|
||||
var rect_max := center + size * 0.5
|
||||
assert_true(rect_min.x >= bounds.position.x - EPS and rect_min.y >= bounds.position.y - EPS,
|
||||
"贴花左下角 %s 跑到模型包围盒 %s 外面了" % [rect_min, bounds])
|
||||
assert_true(rect_max.x <= bounds.end.x + EPS and rect_max.y <= bounds.end.y + EPS,
|
||||
"贴花右上角 %s 跑到模型包围盒 %s 外面了 —— 模型换尺度/原点后会这样" % [rect_max, bounds])
|
||||
|
||||
|
||||
func test_face_shader_has_expected_uniforms() -> void:
|
||||
var code := _shader_code()
|
||||
if code.is_empty():
|
||||
return
|
||||
for required in [
|
||||
"body_tex", "body_tint", "surface_roughness", "surface_metallic",
|
||||
"face_center", "face_size", "face_front_edge", "face_flip_y",
|
||||
"base_tex", "base_uv_min", "base_uv_size", "paint_tex",
|
||||
]:
|
||||
assert_true(code.contains("uniform ") and code.contains(required), "shader 少了 uniform %s" % required)
|
||||
assert_true(code.contains("texture(body_tex, UV)"),
|
||||
"shader 必须用 body_tex 采样全身底色,否则脸和身体不是同一块皮肤")
|
||||
assert_true(code.contains("varying vec3 object_position") and code.contains("object_position.xy"),
|
||||
"贴花必须用模型本地坐标定位,否则又会被 UV 孤岛边界卡住")
|
||||
assert_true(code.contains("normalize(object_normal).z"),
|
||||
"贴花必须按「法线朝前」筛一遍,否则背面的点也会画上表情")
|
||||
# 模型正面是本地 +Z(尾巴在 -Z 那一侧),写成 -normalize 表情就跑到背上去了
|
||||
assert_true(not code.contains("-normalize(object_normal).z"),
|
||||
"正面遮罩写反了:模型正面是本地 +Z,-normalize 会让表情出现在背上")
|
||||
assert_true(code.contains("ROUGHNESS = surface_roughness"), "shader 必须复用 mat_body 的 roughness")
|
||||
assert_true(code.contains("METALLIC = surface_metallic"), "shader 必须复用 mat_body 的 metallic")
|
||||
# alpha 只代表墨线覆盖率,不能写进空间 ALPHA,否则身体面片会被抠出洞
|
||||
assert_true(not code.contains("ALPHA ="), "面部 shader 写了 ALPHA,会把面片渲染成镂空")
|
||||
|
||||
|
||||
func test_face_shader_copies_body_pbr_values() -> void:
|
||||
var mesh_node := _body_mesh()
|
||||
if mesh_node == null:
|
||||
return
|
||||
var body_material := mesh_node.mesh.surface_get_material(0) as StandardMaterial3D
|
||||
assert_true(body_material != null, "surface 0 应有 StandardMaterial3D")
|
||||
if body_material == null:
|
||||
return
|
||||
assert_true(absf(body_material.roughness - 0.65) < 0.001,
|
||||
"mat_body roughness 改变:当前 %f;FaceRig 会动态读取它,测试值跟着改" % body_material.roughness)
|
||||
assert_eq(body_material.metallic, 0.0, "mat_body metallic 改变;FaceRig 会动态读取它")
|
||||
|
||||
|
||||
## 内置表情用的是项目里的透明图集副本(原始表情图已随模型一起删掉)。
|
||||
func test_builtin_atlas_is_transparent_ink() -> void:
|
||||
var texture: Texture2D = load(FaceRig.ATLAS_PATH)
|
||||
assert_true(texture != null, "%s 加载失败" % FaceRig.ATLAS_PATH)
|
||||
if texture == null:
|
||||
return
|
||||
var image := texture.get_image()
|
||||
var transparent := false
|
||||
var visible_ink := false
|
||||
for y in range(0, image.get_height(), 8):
|
||||
for x in range(0, image.get_width(), 8):
|
||||
var a := image.get_pixel(x, y).a
|
||||
transparent = transparent or a < 0.05
|
||||
visible_ink = visible_ink or a > 0.5
|
||||
assert_true(transparent, "内置表情图集没有透明背景,可能忘了去蓝底")
|
||||
assert_true(visible_ink, "内置表情图集里没有可见笔画")
|
||||
|
||||
|
||||
func test_builtin_face_uv_tiles_exist() -> void:
|
||||
assert_eq(FaceRig.BUILTIN_EXPRESSIONS.size(), 2, "内置表情清单应包含两张 atlas 表情")
|
||||
if FaceRig.BUILTIN_EXPRESSIONS.size() < 2:
|
||||
return
|
||||
assert_eq(FaceRig.BUILTIN_EXPRESSIONS[0]["id"], "builtin:face_01", "第一个内置表情 ID 改变")
|
||||
assert_eq(FaceRig.BUILTIN_EXPRESSIONS[1]["id"], "builtin:face_02", "第二个内置表情 ID 改变")
|
||||
for entry in FaceRig.BUILTIN_EXPRESSIONS:
|
||||
var rect: Rect2 = entry["rect"]
|
||||
assert_true(rect.position.x >= 0.0 and rect.position.y >= 0.0, "表情图集 rect 起点越界")
|
||||
assert_true(rect.end.x <= 1.0 and rect.end.y <= 1.0, "表情图集 rect 终点越界")
|
||||
|
||||
|
||||
## 联机同步靠这几个方法名,改名了要一起改 —— 改了就不同步,而且不报错
|
||||
func test_face_sync_methods_exist() -> void:
|
||||
var script := load("res://character/player/face/face_rig.gd") as GDScript
|
||||
assert_true(script != null, "face_rig.gd 加载失败")
|
||||
if script == null:
|
||||
return
|
||||
var names: Array[String] = []
|
||||
for m in script.get_script_method_list():
|
||||
names.append(str(m.get("name")))
|
||||
for required in [
|
||||
"_pull_remote_face", "_rpc_request_face", "_rpc_send_face",
|
||||
"get_expression_catalog", "select_expression", "save_drawing_as_expression",
|
||||
]:
|
||||
assert_true(names.has(required), "face_rig.gd 少了 %s,表情列表/保存/联机可能失效" % required)
|
||||
|
||||
|
||||
## 「没有底图」用运行时创建的 RGBA ImageTexture,而不是磁盘上的全透明 PNG。
|
||||
## Godot 导入器会把整张同 alpha 的 PNG 优化成 RGB,丢掉 alpha;运行时的 Image 则保留。
|
||||
func test_runtime_blank_texture_preserves_alpha() -> void:
|
||||
var image := Image.create(2, 2, false, Image.FORMAT_RGBA8)
|
||||
var texture := ImageTexture.create_from_image(image)
|
||||
assert_true(texture != null, "运行时空白 ImageTexture 创建失败")
|
||||
if texture == null:
|
||||
return
|
||||
var loaded := texture.get_image()
|
||||
assert_eq(loaded.get_format(), Image.FORMAT_RGBA8, "空白贴图被降成了 RGB 格式")
|
||||
assert_eq(loaded.get_pixel(0, 0).a, 0.0, "空白贴图 alpha 应为 0")
|
||||
assert_eq(loaded.get_pixel(1, 1).a, 0.0, "空白贴图中心 alpha 应为 0")
|
||||
|
||||
|
||||
func test_presets_are_square() -> void:
|
||||
var dir := DirAccess.open(PRESET_DIR)
|
||||
assert_true(dir != null, "打不开预设目录 %s" % PRESET_DIR)
|
||||
if dir == null:
|
||||
return
|
||||
var found := 0
|
||||
for f in dir.get_files():
|
||||
if not f.ends_with(".png"):
|
||||
continue
|
||||
found += 1
|
||||
var tex: Texture2D = load(PRESET_DIR + "/" + f)
|
||||
assert_true(tex != null, "预设 %s 加载失败" % f)
|
||||
if tex == null:
|
||||
continue
|
||||
var s := tex.get_size()
|
||||
assert_eq(int(s.x), int(s.y), "预设 %s 不是正方形(%s),贴到脸上会拉伸" % [f, s])
|
||||
var image := tex.get_image()
|
||||
var transparent := false
|
||||
var visible_ink := false
|
||||
for y in range(0, image.get_height(), 4):
|
||||
for x in range(0, image.get_width(), 4):
|
||||
var a := image.get_pixel(x, y).a
|
||||
transparent = transparent or a < 0.05
|
||||
visible_ink = visible_ink or a > 0.5
|
||||
assert_true(transparent, "预设 %s 没有透明背景,可能忘了去蓝底" % f)
|
||||
assert_true(visible_ink, "预设 %s 没有可见笔画" % f)
|
||||
assert_gt(found, 0, "%s 里一张预设都没有" % PRESET_DIR)
|
||||
@@ -1 +0,0 @@
|
||||
uid://brw0ntpgcol2o
|
||||
@@ -1,237 +0,0 @@
|
||||
@tool
|
||||
extends McpTestSuite
|
||||
## 回归测试:Player 的同步配置、动作库,以及「移动 / 表现」的组件边界。
|
||||
##
|
||||
## 三处典型的「静默失效」——都表现为「人站着不动」且不打任何错误:
|
||||
## 1. player_sync.tres 键名写错 / replication_mode 落到 NEVER → 远端收不到字段;
|
||||
## 2. track 路径带一层模型目录名前缀,换模型/改层级后整组失效;
|
||||
## 3. glb 片段的 loop_mode 默认是 NONE,idle/walk/crouch/run/sneak 播一遍就定在最后一帧。
|
||||
## (loop 由 PlayerAnimator._ready() 在运行时补,编辑器测试覆盖不到,靠实机验证。)
|
||||
|
||||
const CONFIG_PATH := "res://character/player/player_sync.tres"
|
||||
const PLAYER_SCENE_PATH := "res://character/player/player.tscn"
|
||||
|
||||
## 从 player.gd 抽出来的动画状态机。控制器只通过它间接驱动动画。
|
||||
## 表现层脚本就直接挂在 AnimationPlayer 上,所以这两条路径指向同一个节点。
|
||||
const ANIMATOR_PATH := "Body/AnimationPlayer"
|
||||
const ANIMATOR_SCRIPT := "res://character/player/player_animator.gd"
|
||||
## 真正干活的动画器。表现层脚本挂在它身上。
|
||||
const ANIM_PLAYER_PATH := "Body/AnimationPlayer"
|
||||
|
||||
## player_animator.gd 的 ANIM_CLIPS 写死了这几个名字,少一个对应状态就会静默不动。
|
||||
const REQUIRED_CLIPS: Array[String] = ["idle", "walk", "crouch", "run", "sneak", "jump", "fall"]
|
||||
|
||||
## CROUCH / SNEAK 两个状态唯一的入口 —— 控制器读的就是这个动作。
|
||||
const CROUCH_ACTION := "crouch"
|
||||
|
||||
## 必须被复制到远端的字段。远端物理与输入都关着,全靠这几个值回放。
|
||||
const REQUIRED_SYNCED: Array[String] = [
|
||||
".:position",
|
||||
"Body:rotation",
|
||||
".:health",
|
||||
"Body/AnimationPlayer:anim_state",
|
||||
"Body/AnimationPlayer:anim_speed",
|
||||
]
|
||||
|
||||
|
||||
func suite_name() -> String:
|
||||
return "net"
|
||||
|
||||
|
||||
func test_config_has_required_synced_fields() -> void:
|
||||
var config := _load_config()
|
||||
assert_true(config != null, "%s 未能加载为 SceneReplicationConfig" % CONFIG_PATH)
|
||||
if config == null:
|
||||
return
|
||||
var paths := config.get_properties()
|
||||
for required in REQUIRED_SYNCED:
|
||||
assert_true(
|
||||
config.has_property(NodePath(required)),
|
||||
"缺少同步字段 %s,实际为 %s" % [required, paths]
|
||||
)
|
||||
|
||||
|
||||
func test_config_replication_mode_is_always() -> void:
|
||||
var config := _load_config()
|
||||
assert_true(config != null, "%s 未能加载" % CONFIG_PATH)
|
||||
if config == null:
|
||||
return
|
||||
var paths := config.get_properties()
|
||||
assert_gt(paths.size(), 0, "同步字段为空,远端玩家不会移动")
|
||||
for path in paths:
|
||||
assert_eq(
|
||||
config.property_get_replication_mode(path),
|
||||
SceneReplicationConfig.REPLICATION_MODE_ALWAYS,
|
||||
"%s 的 replication_mode 不是 ALWAYS(改成 NEVER 会静默断掉同步)" % path
|
||||
)
|
||||
|
||||
|
||||
func test_player_scene_wires_synchronizer() -> void:
|
||||
var player := _instantiate_player()
|
||||
if player == null:
|
||||
return
|
||||
var sync := player.get_node_or_null("Sync") as MultiplayerSynchronizer
|
||||
assert_true(sync != null, "Player 下缺少 Sync 节点")
|
||||
if sync == null:
|
||||
return
|
||||
assert_true(sync.replication_config != null, "Sync 没有绑定 SceneReplicationConfig")
|
||||
assert_eq(sync.root_path, ^"..", "Sync.root_path 应为 .. (相对 Player)")
|
||||
if sync.replication_config != null:
|
||||
assert_eq(
|
||||
sync.replication_config.resource_path,
|
||||
CONFIG_PATH,
|
||||
"Sync 绑定的是别的同步配置:%s" % sync.replication_config.resource_path
|
||||
)
|
||||
|
||||
|
||||
## 组件边界:动画状态机必须是挂在 Body 下的节点,而不是塞回 player.gd。
|
||||
## 有人把 anim_state / anim_speed 挪回控制器时,这条会失败。
|
||||
func test_player_scene_has_animator_component() -> void:
|
||||
var player := _instantiate_player()
|
||||
if player == null:
|
||||
return
|
||||
var animator := player.get_node_or_null(ANIMATOR_PATH)
|
||||
assert_true(animator != null, "Player 下缺少 %s(动画状态机应独立于 player.gd)" % ANIMATOR_PATH)
|
||||
var player_script: Script = player.get_script()
|
||||
assert_true(player_script != null, "Player 没有挂脚本")
|
||||
if player_script != null:
|
||||
var member_names: Array[String] = []
|
||||
for member in player_script.get_script_property_list():
|
||||
member_names.append(str(member.get("name")))
|
||||
assert_false(member_names.has("anim_state"), "anim_state 应该留在 PlayerAnimator 上,不该回到 player.gd")
|
||||
assert_false(member_names.has("anim_speed"), "anim_speed 应该留在 PlayerAnimator 上,不该回到 player.gd")
|
||||
if animator == null:
|
||||
return
|
||||
var script: Script = animator.get_script()
|
||||
assert_true(script != null, "%s 没有挂脚本" % ANIMATOR_PATH)
|
||||
if script != null:
|
||||
assert_eq(script.resource_path, ANIMATOR_SCRIPT, "%s 挂的不是动画状态机脚本" % ANIMATOR_PATH)
|
||||
|
||||
|
||||
## PlayerAnimator 没接线时在父节点的子节点里按类型找 AnimationPlayer。
|
||||
## 编辑器里跑不了它的 _ready()(非 @tool 脚本),所以这里退一步、用同样的规则找一遍:
|
||||
## 改名或挪位置会让这条失败。
|
||||
func test_animator_auto_find_target_exists() -> void:
|
||||
var player := _instantiate_player()
|
||||
if player == null:
|
||||
return
|
||||
var body := player.get_node_or_null("Body")
|
||||
assert_true(body != null, "Player 下缺少 Body")
|
||||
if body == null:
|
||||
return
|
||||
var found := body.find_child("AnimationPlayer", true, false) as AnimationPlayer
|
||||
assert_true(found != null, "Body 子树里找不到名为 AnimationPlayer 的节点,PlayerAnimator 的自动接线会失败")
|
||||
assert_true(
|
||||
player.get_node_or_null(ANIM_PLAYER_PATH) != null,
|
||||
"动画器不在预期路径 %s" % ANIM_PLAYER_PATH
|
||||
)
|
||||
|
||||
|
||||
func test_required_animation_clips_exist() -> void:
|
||||
var anim_player := _find_animation_player()
|
||||
if anim_player == null:
|
||||
return
|
||||
for clip in REQUIRED_CLIPS:
|
||||
assert_true(
|
||||
_find_clip(anim_player, clip) != null,
|
||||
"动作库里缺少片段 %s(PlayerAnimator 的 ANIM_CLIPS 会指向不存在的片段)" % clip
|
||||
)
|
||||
|
||||
|
||||
## crouch 动作是 CROUCH / SNEAK 唯一的入口;动作没了按 Ctrl 没反应,而且不报错。
|
||||
## 编辑器进程的 InputMap 只带内建动作,得先按引擎启动那套加载一次,否则这条必然假红。
|
||||
func test_crouch_action_exists() -> void:
|
||||
InputMap.load_from_project_settings()
|
||||
assert_true(InputMap.has_action(CROUCH_ACTION), "InputMap 里没有 %s 动作" % CROUCH_ACTION)
|
||||
if not InputMap.has_action(CROUCH_ACTION):
|
||||
return
|
||||
var bound_to_ctrl := false
|
||||
for event in InputMap.action_get_events(CROUCH_ACTION):
|
||||
if event is InputEventKey and (event as InputEventKey).keycode == KEY_CTRL:
|
||||
bound_to_ctrl = true
|
||||
assert_true(bound_to_ctrl, "%s 没有绑定 Ctrl" % CROUCH_ACTION)
|
||||
|
||||
|
||||
## 片段是从 glb 里拷出来的,track 路径带层级前缀。换模型/改层级后整组 track 会一起失效,
|
||||
## 而 AnimationPlayer 不报任何错 —— 现象是「人站着不动」。用测试钉住路径真的能解析。
|
||||
func test_animation_track_paths_resolve() -> void:
|
||||
var anim_player := _find_animation_player()
|
||||
if anim_player == null:
|
||||
return
|
||||
var root := anim_player.get_node_or_null(anim_player.root_node)
|
||||
assert_true(root != null, "AnimationPlayer.root_node 解析不到:%s" % anim_player.root_node)
|
||||
if root == null:
|
||||
return
|
||||
|
||||
for clip in REQUIRED_CLIPS:
|
||||
var anim := _find_clip(anim_player, clip)
|
||||
if anim == null:
|
||||
continue
|
||||
assert_gt(anim.get_track_count(), 0, "%s 片段没有任何 track" % clip)
|
||||
for i in anim.get_track_count():
|
||||
var track_path := anim.track_get_path(i)
|
||||
# 不能直接 get_node(track_path):路径含 ":骨骼名" 属性部分,get_node 只认节点段
|
||||
var owner_node := root.get_node_or_null(NodePath(track_path.get_concatenated_names()))
|
||||
assert_true(
|
||||
owner_node != null,
|
||||
"%s 的 track %s 在 %s 下解析不到(动画会静默无效)" % [clip, track_path, anim_player.root_node]
|
||||
)
|
||||
if owner_node == null:
|
||||
continue
|
||||
# 属性段是骨骼名,模型换皮时要确保骨骼名没变
|
||||
var bone := str(track_path.get_concatenated_subnames())
|
||||
if owner_node is Skeleton3D and not bone.is_empty():
|
||||
assert_true(
|
||||
(owner_node as Skeleton3D).find_bone(bone) != -1,
|
||||
"%s 引用了骨骼 %s,但骨架里没有这根骨头(模型换过了?)" % [clip, bone]
|
||||
)
|
||||
|
||||
|
||||
## 入口场景必须是开始菜单 —— 否则启动就直接落在世界里,还顶着 Net 那个调试面板。
|
||||
## 这条防的是「有人把 run/main_scene 改回世界」。
|
||||
func test_main_scene_is_the_menu() -> void:
|
||||
var configured := str(ProjectSettings.get_setting("application/run/main_scene", ""))
|
||||
assert_true(not configured.is_empty(), "project.godot 没设 application/run/main_scene")
|
||||
if configured.is_empty():
|
||||
return
|
||||
# 存的是 uid 也可能是 res:// 路径,两种都认
|
||||
var path := configured
|
||||
if configured.begins_with("uid://"):
|
||||
path = ResourceUID.get_id_path(ResourceUID.text_to_id(configured))
|
||||
assert_true(path.ends_with("ui/main_menu.tscn"),
|
||||
"入口场景是 %s,不是开始菜单 —— 启动会直接进世界" % path)
|
||||
|
||||
|
||||
## 片段在 AnimationPlayer 里按「库名/片段名」存(导入时每个 glb 各自成一个库),
|
||||
## 短名 has_animation() / get_animation() 一律查不到 —— 拿短名断言会假红,必须按库扫一遍。
|
||||
func _find_clip(anim_player: AnimationPlayer, clip: String) -> Animation:
|
||||
for lib_name in anim_player.get_animation_library_list():
|
||||
var lib := anim_player.get_animation_library(lib_name)
|
||||
if lib != null and lib.has_animation(clip):
|
||||
return lib.get_animation(clip)
|
||||
return null
|
||||
|
||||
|
||||
func _find_animation_player() -> AnimationPlayer:
|
||||
var player := _instantiate_player()
|
||||
if player == null:
|
||||
return null
|
||||
var anim_player := player.get_node_or_null(ANIM_PLAYER_PATH) as AnimationPlayer
|
||||
assert_true(anim_player != null, "找不到 %s" % ANIM_PLAYER_PATH)
|
||||
return anim_player
|
||||
|
||||
|
||||
## 直接用 load() 拿到的是编辑器进程里的内存副本 —— 外部刚改过磁盘时它可能还是旧的,
|
||||
## 测试会因此假绿或假红。这里强制按磁盘重读,断言的是文件本身。
|
||||
func _load_config() -> SceneReplicationConfig:
|
||||
return ResourceLoader.load(CONFIG_PATH, "", ResourceLoader.CACHE_MODE_REPLACE) as SceneReplicationConfig
|
||||
|
||||
|
||||
func _instantiate_player() -> Node:
|
||||
var packed := load(PLAYER_SCENE_PATH) as PackedScene
|
||||
assert_true(packed != null, "%s 未能加载" % PLAYER_SCENE_PATH)
|
||||
if packed == null:
|
||||
return null
|
||||
var player := packed.instantiate()
|
||||
track(player)
|
||||
return player
|
||||
@@ -1 +0,0 @@
|
||||
uid://nvx3ojicfxh5
|
||||
@@ -1,224 +0,0 @@
|
||||
@tool
|
||||
extends McpTestSuite
|
||||
## 回归测试:滑索的几何契约,以及它和玩家控制器的接线。
|
||||
##
|
||||
## 滑索坏了不会报错,只会「人挂在半空 / 往高处滑 / 挂不上」—— 全是静默失效。
|
||||
## 所以这里钉死的是几何约定:
|
||||
## 1. 曲线两端必须正好落在两个锚点上,否则「看到的缆」和「能挂的点」是两回事;
|
||||
## 2. sag 是「中点下垂深度」,不是别的什么比例;
|
||||
## 3. tangent() 从 A 指向 B,反了人会往高处滑;
|
||||
## 4. closest_point().dir 永远指向低的那一端,滑索只能往下滑;
|
||||
## 5. ride_point() 在缆下方 hang_offset 处,否则人卡在缆里。
|
||||
##
|
||||
## Zipline 是 @tool 脚本,缆绳网格在 _ready() 里现造 —— 所以实例必须真的进树,
|
||||
## 不能像纯数据那样只 instantiate() 就断言。
|
||||
|
||||
const ZIPLINE_SCENE_PATH := "res://world/zipline/zipline.tscn"
|
||||
const ZIPLINE_SCRIPT_PATH := "res://world/zipline/zipline.gd"
|
||||
const PLAYER_SCENE_PATH := "res://character/player/player.tscn"
|
||||
const INTERACT_ACTION := "interact"
|
||||
## 挂在编辑中场景下。前缀 _McpTest 是测试运行器兜底清理认的标记
|
||||
const TEST_NODE_NAME := "_McpTestZipline"
|
||||
## 测试自己造的父节点:不带任何变换,免得编辑中场景的缩放漏进来
|
||||
const TEST_PARENT_NAME := "_McpTestZiplineHolder"
|
||||
|
||||
var _zip: Zipline = null
|
||||
|
||||
|
||||
func suite_name() -> String:
|
||||
return "zipline"
|
||||
|
||||
|
||||
func suite_setup(_ctx: Dictionary) -> void:
|
||||
if EditorInterface.get_edited_scene_root() == null:
|
||||
skip_suite("需要先打开一个场景:Zipline 是 @tool,进树才会生成缆绳网格")
|
||||
return
|
||||
# 先试装一次再丢掉 —— 否则 13 个测试会各自撞同一堵墙,报 13 条一样的错
|
||||
var probe := _instantiate_zipline()
|
||||
if probe == null:
|
||||
fail_setup("%s 加载/实例化失败,或根节点没挂 Zipline 脚本" % ZIPLINE_SCENE_PATH)
|
||||
return
|
||||
probe.free()
|
||||
|
||||
|
||||
## 每个测试拿一个干净实例:有些测试会故意篡改锚点,共享实例会让结果依赖执行顺序。
|
||||
## 挂在**编辑器根节点**下的测试父节点里,而不是编辑中场景根下 ——
|
||||
## 滑索的几何是在它自己的空间里定义的,挂在场景根下会被那个场景的变换带偏
|
||||
## (player.tscn 的根节点就带 0.2 缩放,实测把中点下垂从 1.2 变成 0.24)。
|
||||
func setup() -> void:
|
||||
_zip = _instantiate_zipline()
|
||||
if _zip != null:
|
||||
Engine.get_main_loop().root.add_child(_make_holder(Vector3.ONE, _zip))
|
||||
|
||||
|
||||
func teardown() -> void:
|
||||
if is_instance_valid(_zip) and _zip.get_parent() != null:
|
||||
_zip.get_parent().remove_child(_zip)
|
||||
_zip.free()
|
||||
_zip = null
|
||||
|
||||
|
||||
# ---------------- 场景接线 ----------------
|
||||
|
||||
func test_scene_root_script() -> void:
|
||||
var script: Script = _zip.get_script()
|
||||
assert_true(script != null, "滑索根节点没有脚本")
|
||||
if script != null:
|
||||
assert_eq(script.resource_path, ZIPLINE_SCRIPT_PATH, "滑索根节点挂的不是 zipline.gd")
|
||||
|
||||
|
||||
func test_anchors_exist() -> void:
|
||||
assert_true(_zip.anchor_a != null, "缺少 AnchorA(Marker3D),缆绳没有起点")
|
||||
assert_true(_zip.anchor_b != null, "缺少 AnchorB(Marker3D),缆绳没有终点")
|
||||
|
||||
|
||||
## 缆绳网格是 _ready() 里现造的,不是场景里的节点。它没生成的话游戏里什么都看不见,
|
||||
## 但不会报任何错 —— 所以长度和网格都要断言。
|
||||
func test_cable_mesh_generated() -> void:
|
||||
assert_gt(_zip.length, 0.0, "缆绳长度为 0:锚点重合,或者 _rebuild_cable() 没跑到")
|
||||
var cable := _zip.get_node_or_null("Cable") as MeshInstance3D
|
||||
assert_true(cable != null, "找不到 Cable 子节点(应该在 _ready() 里生成)")
|
||||
if cable != null:
|
||||
assert_true(cable.mesh != null, "Cable 没有网格:SurfaceTool 没 commit 成功")
|
||||
|
||||
|
||||
## 玩家控制器用 $InteractHint 拿提示节点,被改名或删掉会在运行时炸。
|
||||
func test_player_scene_has_interact_hint() -> void:
|
||||
var packed := ResourceLoader.load(PLAYER_SCENE_PATH, "", ResourceLoader.CACHE_MODE_REPLACE) as PackedScene
|
||||
assert_true(packed != null, "%s 加载失败" % PLAYER_SCENE_PATH)
|
||||
if packed == null:
|
||||
return
|
||||
var player := packed.instantiate()
|
||||
track(player)
|
||||
var hint := player.get_node_or_null("InteractHint") as Label3D
|
||||
assert_true(hint != null, "Player 下缺少 InteractHint(Label3D),$InteractHint 会取到 null")
|
||||
if hint != null:
|
||||
assert_false(hint.visible, "InteractHint 默认应该是隐藏的")
|
||||
|
||||
|
||||
## 挂索/脱手都走 InputMap 的 interact 动作;动作没了按 E 没反应,而且不报错。
|
||||
## 编辑器进程的 InputMap 只带内建动作,项目动作要等游戏进程启动时才 load 进去 ——
|
||||
## 不先补这一步,这条断言在编辑器测试环境里必然假红。
|
||||
func test_interact_action_exists() -> void:
|
||||
InputMap.load_from_project_settings()
|
||||
assert_true(InputMap.has_action(INTERACT_ACTION), "InputMap 里没有 %s 动作" % INTERACT_ACTION)
|
||||
if not InputMap.has_action(INTERACT_ACTION):
|
||||
return
|
||||
var bound_to_e := false
|
||||
for event in InputMap.action_get_events(INTERACT_ACTION):
|
||||
if event is InputEventKey and (event as InputEventKey).keycode == KEY_E:
|
||||
bound_to_e = true
|
||||
assert_true(bound_to_e, "%s 没有绑定 E 键" % INTERACT_ACTION)
|
||||
|
||||
|
||||
# ---------------- 几何契约 ----------------
|
||||
|
||||
func test_ends_match_anchors() -> void:
|
||||
assert_true(_zip.sample(0.0).is_equal_approx(_zip.anchor_a.global_position), "sample(0) 不在 AnchorA 上")
|
||||
assert_true(_zip.sample(1.0).is_equal_approx(_zip.anchor_b.global_position), "sample(1) 不在 AnchorB 上")
|
||||
|
||||
|
||||
## sag 的定义是「中点相对两点连线中点的下垂深度」。这条一错,缆绳的弧度和手感全变。
|
||||
func test_sag_is_midpoint_dip() -> void:
|
||||
var straight_mid := (_zip.anchor_a.global_position + _zip.anchor_b.global_position) * 0.5
|
||||
var dip := straight_mid.y - _zip.sample(0.5).y
|
||||
assert_true(is_equal_approx(dip, _zip.sag), "中点下垂 %.4f 与 sag %.4f 不符" % [dip, _zip.sag])
|
||||
|
||||
|
||||
## 方向反了人会往高处滑 —— 最容易写错、又最难看出来的一处。
|
||||
func test_tangent_points_from_a_to_b() -> void:
|
||||
var along := (_zip.anchor_b.global_position - _zip.anchor_a.global_position).normalized()
|
||||
for t in [0.0, 0.25, 0.5, 0.75, 1.0]:
|
||||
assert_gt(_zip.tangent(t).dot(along), 0.9, "tangent(%s) 没从 A 指向 B" % t)
|
||||
|
||||
|
||||
## 父节点带缩放时,整条缆要跟着一起缩 —— 形状(下垂比例)不能走样。
|
||||
## 这里踩过:曲线用 global 锚点 lerp、却加未缩放的 sag,父节点一缩放形状就偏,
|
||||
## 挂在带缩放的父节点下 tangent 会歪到 27°,直接把人往错方向送。
|
||||
func test_shape_survives_parent_scale() -> void:
|
||||
var packed := load(ZIPLINE_SCENE_PATH) as PackedScene
|
||||
if packed == null:
|
||||
return
|
||||
var zip := packed.instantiate() as Zipline
|
||||
if zip == null:
|
||||
return
|
||||
zip.name = TEST_NODE_NAME
|
||||
Engine.get_main_loop().root.add_child(_make_holder(Vector3(0.2, 0.2, 0.2), zip))
|
||||
var along := (zip.anchor_b.global_position - zip.anchor_a.global_position).normalized()
|
||||
for t in [0.0, 0.5, 1.0]:
|
||||
assert_gt(zip.tangent(t).dot(along), 0.9, "缩放父节点下 tangent(%s) 形状走样了" % t)
|
||||
# 中点的世界下垂量应该是 sag × 父节点缩放
|
||||
var dip := zip.anchor_a.global_position.lerp(zip.anchor_b.global_position, 0.5).distance_to(zip.sample(0.5))
|
||||
assert_true(absf(dip - zip.sag * 0.2) < 0.01,
|
||||
"缩放后中点下垂 %.4f,期望 sag×0.2 = %.4f" % [dip, zip.sag * 0.2])
|
||||
|
||||
|
||||
## 玩家挂索靠这个接口。返回的点必须在曲线上,distance 必须是真实欧氏距离。
|
||||
func test_closest_point_lands_on_curve() -> void:
|
||||
var probe := _zip.sample(0.3) + Vector3(1.0, 1.0, 0.0)
|
||||
var info := _zip.closest_point(probe)
|
||||
assert_has_key(info, "t")
|
||||
assert_has_key(info, "dir")
|
||||
assert_has_key(info, "remaining")
|
||||
assert_has_key(info, "distance")
|
||||
assert_true(info.point.is_equal_approx(_zip.sample(info.t)), "返回的 point 不在曲线上")
|
||||
assert_true(is_equal_approx(info.distance, probe.distance_to(info.point)), "distance 不是到 point 的欧氏距离")
|
||||
|
||||
|
||||
## 滑索只能往下滑:出口必须是低的那一端。
|
||||
func test_dir_points_to_lower_end() -> void:
|
||||
assert_true(
|
||||
_zip.anchor_b.global_position.y < _zip.anchor_a.global_position.y,
|
||||
"这个测试假定场景里的 AnchorB 更低"
|
||||
)
|
||||
assert_eq(_zip.closest_point(_zip.sample(0.5)).dir, 1, "B 更低时应该往 B 滑")
|
||||
|
||||
|
||||
## 把 B 抬到比 A 高,出口就该换成 A —— 滑索不会把人往上拽。
|
||||
func test_dir_flips_when_b_is_higher() -> void:
|
||||
var lifted := _zip.anchor_b.global_position
|
||||
lifted.y = _zip.anchor_a.global_position.y + 5.0
|
||||
_zip.anchor_b.global_position = lifted
|
||||
assert_eq(_zip.closest_point(_zip.sample(0.5)).dir, -1, "B 比 A 高时应该往 A 滑")
|
||||
|
||||
|
||||
## 人挂在缆下方,不是在缆上 —— 否则模型会卡在缆里。
|
||||
func test_ride_point_hangs_below_cable() -> void:
|
||||
for t in [0.0, 0.5, 1.0]:
|
||||
var offset := _zip.sample(t) - _zip.ride_point(t)
|
||||
assert_true(
|
||||
offset.is_equal_approx(Vector3.UP * _zip.hang_offset),
|
||||
"ride_point(%s) 不在缆下方 hang_offset 处" % t
|
||||
)
|
||||
|
||||
|
||||
## 越靠近出口,剩余行程越短。玩家用它判断「挂上还有没有得滑」。
|
||||
func test_remaining_shrinks_toward_exit() -> void:
|
||||
var near_start := _zip.closest_point(_zip.sample(0.1))
|
||||
var near_exit := _zip.closest_point(_zip.sample(0.95))
|
||||
assert_gt(near_start.remaining, near_exit.remaining, "剩余行程没有随 t 递减")
|
||||
assert_true(
|
||||
near_exit.remaining < _zip.min_ride,
|
||||
"出口附近的剩余行程应小于 min_ride,否则玩家会挂在终点上反复弹开"
|
||||
)
|
||||
|
||||
|
||||
func _instantiate_zipline() -> Zipline:
|
||||
var packed := ResourceLoader.load(ZIPLINE_SCENE_PATH, "", ResourceLoader.CACHE_MODE_REPLACE) as PackedScene
|
||||
if packed == null:
|
||||
return null
|
||||
var zip := packed.instantiate() as Zipline
|
||||
if zip == null:
|
||||
return null
|
||||
zip.name = TEST_NODE_NAME
|
||||
return zip
|
||||
|
||||
|
||||
## 造一个父节点(可带缩放)并把 node 挂进去,返回那个父节点。测试用它把滑索和编辑中场景隔开。
|
||||
func _make_holder(scale_factor: Vector3, node: Node) -> Node3D:
|
||||
var holder := Node3D.new()
|
||||
holder.name = TEST_PARENT_NAME
|
||||
holder.scale = scale_factor
|
||||
holder.add_child(node)
|
||||
track(holder)
|
||||
return holder
|
||||
@@ -1 +0,0 @@
|
||||
uid://b36lbd1qvlebs
|
||||
@@ -1,118 +0,0 @@
|
||||
@tool
|
||||
class_name Carryable
|
||||
extends RigidBody3D
|
||||
## 可抱起的物品。**只有带这个脚本(并进了 carryable 组)的东西才抱得起来** —— 这就是
|
||||
## 「仅特定物品可抱」的实现方式:门槛不在玩家那边,而在物品自己身上。
|
||||
##
|
||||
## 和 Zipline 一样,手感参数放物品这一侧:不同物品可以有不同的抱持偏移、不同的投掷速度,
|
||||
## 玩家只负责「找到它、把它摆到手上、扔出去」,不定义手感。
|
||||
##
|
||||
## 用法:把这个脚本挂在物品根节点(RigidBody3D)上,再给它加一个 MultiplayerSynchronizer
|
||||
## (配置见 res://world/items/carryable_sync.tres,同步 position / rotation / carrier_peer)。
|
||||
## 参考示例见 res://world/items/carryable_box.tscn。
|
||||
##
|
||||
## 联机只同步一个整数:`carrier_peer`(谁抱着)。
|
||||
## **抱着的时候位置根本不走同步** —— 每一端都从「持有者的抱持点」把物品摆出来,
|
||||
## 结果天然一致,也省掉了「持有者写位置、同步又把位置写回来」的互相打架。
|
||||
## 空着的时候才靠 position / rotation 同步,而且只有服务器在模拟。
|
||||
##
|
||||
## 标了 @tool 是为了能在编辑器里跑测试(非 @tool 脚本在编辑器里是 placeholder,方法调不动);
|
||||
## 运行时行为完全一样。
|
||||
|
||||
## 所有可抱物品都进这个组,玩家靠它找物品
|
||||
const GROUP: StringName = &"carryable"
|
||||
## 玩家节点在世界里以 peer id 命名并进了这个组,靠它找持有者的抱持点
|
||||
const PLAYER_GROUP: StringName = &"net_player"
|
||||
## 抱持点在玩家场景里的相对路径
|
||||
const SOCKET_PATH: String = "CameraRig/CarrySocket"
|
||||
|
||||
@export_group("抱持")
|
||||
## 被抱时相对抱持点的偏移(米)。物品大小不同,靠它对准手心
|
||||
@export var hold_offset: Vector3 = Vector3.ZERO
|
||||
## 被抱时的朝向(度)
|
||||
@export var hold_rotation_deg: Vector3 = Vector3.ZERO
|
||||
|
||||
@export_group("投掷")
|
||||
## 长按蓄满力后的出手速度(米/秒)
|
||||
@export var throw_speed_max: float = 14.0
|
||||
## 轻点时最低出手速度(米/秒)
|
||||
@export var throw_speed_min: float = 5.0
|
||||
|
||||
## 谁抱着它(peer id),0 = 没人抱。
|
||||
## 由服务器改,通过物品自己的 MultiplayerSynchronizer 复制到各端;各端只读。
|
||||
var carrier_peer: int = 0:
|
||||
set(value):
|
||||
if carrier_peer == value:
|
||||
return
|
||||
carrier_peer = value
|
||||
_update_simulation()
|
||||
|
||||
var _saved_collision_layer: int = 1
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
add_to_group(GROUP)
|
||||
_saved_collision_layer = collision_layer
|
||||
_update_simulation()
|
||||
|
||||
|
||||
func can_be_carried() -> bool:
|
||||
return carrier_peer == 0
|
||||
|
||||
|
||||
## 抱着的时候跟着持有者的抱持点走;空着的时候什么都不做(交给物理 / 同步)
|
||||
func _physics_process(_delta: float) -> void:
|
||||
if carrier_peer == 0:
|
||||
return
|
||||
var socket := _find_carrier_socket()
|
||||
if socket != null:
|
||||
apply_hold_transform(socket)
|
||||
|
||||
|
||||
## 把自己摆到抱持点上。偏移和朝向由物品自己决定,玩家不用管。
|
||||
func apply_hold_transform(socket: Node3D) -> void:
|
||||
var socket_basis := socket.global_basis
|
||||
global_position = socket.global_position + socket_basis * hold_offset
|
||||
global_basis = socket_basis * Basis.from_euler(hold_rotation_deg * PI / 180.0)
|
||||
|
||||
|
||||
## 按蓄力比例算出手速度(0 = 轻点,1 = 蓄满)
|
||||
func throw_speed(charge: float) -> float:
|
||||
return lerpf(throw_speed_min, throw_speed_max, clampf(charge, 0.0, 1.0))
|
||||
|
||||
|
||||
## 请求抱起。客户端用 rpc_id(1, ...) 发给服务器仲裁(先到先得),单机时服务器直接执行。
|
||||
## 服务器只改 `carrier_peer` 这一个值,其余交给同步 —— 各端看到的状态因此必然一致。
|
||||
@rpc("any_peer", "call_remote", "reliable")
|
||||
func request_pickup(peer_id: int) -> void:
|
||||
if not multiplayer.is_server() or carrier_peer != 0:
|
||||
return
|
||||
carrier_peer = peer_id
|
||||
|
||||
|
||||
## 放下 / 扔出。持有者算好初速度发给服务器:服务器收回模拟权,让物品带着这个速度接着飞。
|
||||
@rpc("any_peer", "call_remote", "reliable")
|
||||
func request_release(velocity: Vector3) -> void:
|
||||
if not multiplayer.is_server() or carrier_peer == 0:
|
||||
return
|
||||
carrier_peer = 0
|
||||
linear_velocity = velocity
|
||||
|
||||
|
||||
## 抱着时冻结物理、清掉碰撞层(不清会把抱着它的人顶飞);
|
||||
## 空着时只有服务器模拟,其余端冻住跟着同步过来的位置走,免得各端各算一套。
|
||||
func _update_simulation() -> void:
|
||||
if carrier_peer != 0:
|
||||
freeze = true
|
||||
collision_layer = 0
|
||||
return
|
||||
collision_layer = _saved_collision_layer
|
||||
freeze = not multiplayer.is_server()
|
||||
|
||||
|
||||
## 找持有者的抱持点。找不到就什么都不做 —— 远端玩家还没生成时会出现这种情况。
|
||||
func _find_carrier_socket() -> Node3D:
|
||||
for node in get_tree().get_nodes_in_group(PLAYER_GROUP):
|
||||
if str(node.name) == str(carrier_peer):
|
||||
return node.get_node_or_null(SOCKET_PATH) as Node3D
|
||||
return null
|
||||
@@ -1 +0,0 @@
|
||||
uid://bdo6ee5tmvnc2
|
||||
@@ -1,28 +0,0 @@
|
||||
[gd_scene load_steps=6 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://world/items/carryable.gd" id="1_carryable"]
|
||||
[ext_resource type="SceneReplicationConfig" path="res://world/items/carryable_sync.tres" id="2_sync"]
|
||||
|
||||
[sub_resource type="BoxMesh" id="BoxMesh_body"]
|
||||
size = Vector3(0.6, 0.6, 0.6)
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_box"]
|
||||
albedo_color = Color(0.92, 0.6, 0.25, 1)
|
||||
roughness = 0.7
|
||||
|
||||
[sub_resource type="BoxShape3D" id="BoxShape3D_body"]
|
||||
size = Vector3(0.6, 0.6, 0.6)
|
||||
|
||||
[node name="CarryableBox" type="RigidBody3D"]
|
||||
mass = 2.0
|
||||
script = ExtResource("1_carryable")
|
||||
|
||||
[node name="Mesh" type="MeshInstance3D" parent="."]
|
||||
mesh = SubResource("BoxMesh_body")
|
||||
surface_material_override/0 = SubResource("StandardMaterial3D_box")
|
||||
|
||||
[node name="Collision" type="CollisionShape3D" parent="."]
|
||||
shape = SubResource("BoxShape3D_body")
|
||||
|
||||
[node name="Sync" type="MultiplayerSynchronizer" parent="."]
|
||||
replication_config = ExtResource("2_sync")
|
||||
@@ -1,12 +0,0 @@
|
||||
[gd_resource type="SceneReplicationConfig" format=3]
|
||||
|
||||
[resource]
|
||||
properties/0/path = NodePath(".:position")
|
||||
properties/0/spawn = true
|
||||
properties/0/replication_mode = 1
|
||||
properties/1/path = NodePath(".:rotation")
|
||||
properties/1/spawn = true
|
||||
properties/1/replication_mode = 1
|
||||
properties/2/path = NodePath(".:carrier_peer")
|
||||
properties/2/spawn = true
|
||||
properties/2/replication_mode = 1
|
||||
@@ -2,26 +2,112 @@
|
||||
|
||||
[ext_resource type="Script" uid="uid://11rbhffe5dhv" path="res://world/main.gd" id="1_hyim0"]
|
||||
[ext_resource type="PackedScene" uid="uid://bekuuw153mv03" path="res://character/player/player.tscn" id="2_4f7fj"]
|
||||
[ext_resource type="Sky" uid="uid://bi632suyifms5" path="res://world/sky/cartoon_sky.tres" id="2_cartoon"]
|
||||
[ext_resource type="PackedScene" path="res://world/ocean/sea.tscn" id="3_sea"]
|
||||
[ext_resource type="PackedScene" uid="uid://12qonfnowpas" path="res://world/zipline/zipline.tscn" id="6_i1eo7"]
|
||||
[ext_resource type="PackedScene" path="res://world/items/carryable_box.tscn" id="7_carrybox"]
|
||||
[ext_resource type="Shader" uid="uid://cvd0x7lon1ur6" path="res://addons/sky_3d/shaders/SkyMaterial.gdshader" id="5_8q2wj"]
|
||||
[ext_resource type="Texture2D" uid="uid://cfqk60lpl5ljv" path="res://addons/sky_3d/assets/resources/SNoise.tres" id="6_60vd7"]
|
||||
[ext_resource type="Texture2D" uid="uid://cecwdqjol0ckd" path="res://addons/sky_3d/assets/textures/noiseClouds.png" id="7_e63cu"]
|
||||
[ext_resource type="PackedScene" uid="uid://44re8f0ce0kc" path="res://world/main2.tscn" id="7_gu74f"]
|
||||
[ext_resource type="Texture2D" uid="uid://2r8ylu6rg5dp" path="res://addons/sky_3d/assets/thirdparty/textures/moon/MoonMap.png" id="8_s8tpt"]
|
||||
[ext_resource type="Texture2D" uid="uid://djpfuyxkryegn" path="res://addons/sky_3d/assets/textures/noise.jpg" id="9_r4j7u"]
|
||||
[ext_resource type="Texture2D" uid="uid://bm7dot7t7u1q4" path="res://addons/sky_3d/assets/thirdparty/textures/milkyway/StarField.jpg" id="10_h1iki"]
|
||||
[ext_resource type="Texture2D" uid="uid://c1vwcdcdvb74a" path="res://addons/sky_3d/assets/thirdparty/textures/milkyway/Milkyway.jpg" id="11_nikgi"]
|
||||
[ext_resource type="Script" uid="uid://deo0gxkmk2ahd" path="res://addons/sky_3d/src/Sky3D.gd" id="12_d6btn"]
|
||||
[ext_resource type="Script" uid="uid://b4tkplvh7fq40" path="res://addons/sky_3d/src/SkyDome.gd" id="13_wc2es"]
|
||||
[ext_resource type="Script" uid="uid://cjmkcj8hwm7ts" path="res://addons/sky_3d/src/TimeOfDay.gd" id="14_gk3u8"]
|
||||
|
||||
[sub_resource type="Environment" id="Environment_gu74f"]
|
||||
[sub_resource type="BoxShape3D" id="BoxShape3D_cmo1i"]
|
||||
|
||||
[sub_resource type="BoxMesh" id="BoxMesh_i1eo7"]
|
||||
|
||||
[sub_resource type="ShaderMaterial" id="ShaderMaterial_sgblq"]
|
||||
shader = ExtResource("5_8q2wj")
|
||||
shader_parameter/sky_visible = true
|
||||
shader_parameter/color_correction = Vector2(0, 1)
|
||||
shader_parameter/ground_color = Color(0.3, 0.3, 0.3, 1)
|
||||
shader_parameter/horizon_offset = 0.0
|
||||
shader_parameter/atm_darkness = 0.5
|
||||
shader_parameter/atm_sun_intensity = 18.0
|
||||
shader_parameter/atm_day_tint = Color(0.807843, 0.909804, 1, 1)
|
||||
shader_parameter/atm_horizon_light_tint = Color(0.980392, 0.635294, 0.462745, 1)
|
||||
shader_parameter/atm_night_tint = Color(0, 0, 0, 0)
|
||||
shader_parameter/atm_level_params = Vector3(1, 0, 0)
|
||||
shader_parameter/atm_thickness = 0.7
|
||||
shader_parameter/atm_beta_ray = Vector3(5.804544e-06, 1.3562913e-05, 3.311258e-05)
|
||||
shader_parameter/atm_beta_mie = Vector3(3.038e-08, 3.038e-08, 3.038e-08)
|
||||
shader_parameter/sun_light_color = Color(1, 1, 1, 1)
|
||||
shader_parameter/sun_disk_color = Color(0.996094, 0.541334, 0.140076, 1)
|
||||
shader_parameter/sun_disk_intensity = 30.0
|
||||
shader_parameter/sun_disk_size = 0.02
|
||||
shader_parameter/atm_sun_mie_tint = Color(1, 1, 1, 1)
|
||||
shader_parameter/atm_sun_mie_intensity = 1.0
|
||||
shader_parameter/atm_sun_partial_mie_phase = Vector3(0.36, 1.64, 1.6)
|
||||
shader_parameter/moon_color = Color(1, 1, 1, 1)
|
||||
shader_parameter/moon_texture = ExtResource("8_s8tpt")
|
||||
shader_parameter/moon_texture_alignment = Vector3(7, 1.4, 4.8)
|
||||
shader_parameter/moon_texture_flip_u = false
|
||||
shader_parameter/moon_texture_flip_v = false
|
||||
shader_parameter/moon_size = 0.07
|
||||
shader_parameter/atm_moon_mie_tint = Color(0.137255, 0.184314, 0.292196, 1)
|
||||
shader_parameter/atm_moon_mie_intensity = 0.0
|
||||
shader_parameter/atm_moon_partial_mie_phase = Vector3(0.36, 1.64, 1.6)
|
||||
shader_parameter/starmap_color = Color(0.709804, 0.709804, 0.709804, 0.854902)
|
||||
shader_parameter/starmap_texture = ExtResource("11_nikgi")
|
||||
shader_parameter/starmap_flip_u = false
|
||||
shader_parameter/starmap_flip_v = false
|
||||
shader_parameter/starmap_alignment = Vector3(2.68288, -0.25891, 0.40101)
|
||||
shader_parameter/star_rotation_offset = 9.38899
|
||||
shader_parameter/star_rotation = -10.508823420903425
|
||||
shader_parameter/star_tilt = -1.2915436464758039
|
||||
shader_parameter/star_field_color = Color(1, 1, 1, 1)
|
||||
shader_parameter/star_field_texture = ExtResource("10_h1iki")
|
||||
shader_parameter/star_scintillation = 0.75
|
||||
shader_parameter/star_scintillation_speed = 0.01
|
||||
shader_parameter/noise_tex = ExtResource("9_r4j7u")
|
||||
shader_parameter/clouds_night_color = Color(0.090196, 0.094118, 0.129412, 1)
|
||||
shader_parameter/cirrus_visible = true
|
||||
shader_parameter/cirrus_intensity = 2.0
|
||||
shader_parameter/cirrus_coverage = 0.5
|
||||
shader_parameter/cirrus_thickness = 1.7
|
||||
shader_parameter/cirrus_absorption = 2.0
|
||||
shader_parameter/cirrus_sky_tint_fade = 0.5
|
||||
shader_parameter/cirrus_size = 1.0
|
||||
shader_parameter/cirrus_uv = Vector2(0.16, 0.11)
|
||||
shader_parameter/cirrus_position1 = Vector2(0.06373336, 0.06373336)
|
||||
shader_parameter/cirrus_position2 = Vector2(0.06373336, 0.06373336)
|
||||
shader_parameter/cirrus_texture = ExtResource("6_60vd7")
|
||||
shader_parameter/cumulus_visible = true
|
||||
shader_parameter/cumulus_intensity = 0.6
|
||||
shader_parameter/cumulus_coverage = 0.55
|
||||
shader_parameter/cumulus_thickness = 0.0243
|
||||
shader_parameter/cumulus_absorption = 2.0
|
||||
shader_parameter/cumulus_noise_freq = 2.7
|
||||
shader_parameter/cumulus_sky_tint_fade = 0.0
|
||||
shader_parameter/cumulus_size = 0.5
|
||||
shader_parameter/cumulus_position = Vector2(0.3186651, 0.3186651)
|
||||
shader_parameter/cumulus_texture = ExtResource("7_e63cu")
|
||||
shader_parameter/cumulus_partial_mie_phase = Vector3(0.957564, 1.042436, 0.412)
|
||||
shader_parameter/cumulus_mie_intensity = 1.0
|
||||
shader_parameter/show_azimuthal_grid = false
|
||||
shader_parameter/azimuthal_grid_color = Color(0.87058824, 0.72156864, 0.5294118, 1)
|
||||
shader_parameter/azimuthal_grid_rotation_offset = 0.03
|
||||
shader_parameter/show_equatorial_grid = false
|
||||
shader_parameter/equatorial_grid_color = Color(0, 0.75, 1, 1)
|
||||
shader_parameter/equatorial_grid_rotation_offset = 0.03
|
||||
shader_parameter/moon_matrix = Basis(0.5231242, -0.81924146, -0.2349137, 0.3627591, 0.46346128, -0.8084612, 0.77119833, 0.33770853, 0.5396352)
|
||||
|
||||
[sub_resource type="Sky" id="Sky_n7uhj"]
|
||||
sky_material = SubResource("ShaderMaterial_sgblq")
|
||||
|
||||
[sub_resource type="Environment" id="Environment_8l5dh"]
|
||||
background_mode = 2
|
||||
sky = ExtResource("2_cartoon")
|
||||
sky = SubResource("Sky_n7uhj")
|
||||
ambient_light_source = 3
|
||||
ambient_light_color = Color(0.96969974, 0.96969974, 0.96969974, 1)
|
||||
reflected_light_source = 2
|
||||
tonemap_mode = 3
|
||||
tonemap_white = 6.0
|
||||
|
||||
[sub_resource type="BoxShape3D" id="BoxShape3D_qnd6n"]
|
||||
size = Vector3(4, 0.4, 5.5)
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_op3e8"]
|
||||
albedo_color = Color(0.45, 0.31, 0.19, 1)
|
||||
|
||||
[sub_resource type="BoxMesh" id="BoxMesh_vid1e"]
|
||||
size = Vector3(4, 0.4, 5.5)
|
||||
[sub_resource type="CameraAttributesPractical" id="CameraAttributesPractical_6lvsd"]
|
||||
|
||||
[node name="Main" type="Node3D" unique_id=631359418]
|
||||
script = ExtResource("1_hyim0")
|
||||
@@ -30,36 +116,47 @@ player_scene = ExtResource("2_4f7fj")
|
||||
[node name="Sea" parent="." unique_id=1379614066 instance=ExtResource("3_sea")]
|
||||
transform = Transform3D(0.97957206, 0, 0, 0, 0.97957206, 0, 0, 0, 0.97957206, 0, -1.5, 0)
|
||||
|
||||
[node name="Sun" type="DirectionalLight3D" parent="." unique_id=174609621]
|
||||
transform = Transform3D(0.8660248, 0.3830344, -0.32138094, 0, 0.6427605, 0.76606715, 0.5000011, -0.66343313, 0.5566465, 0, 0, 0)
|
||||
light_color = Color(1, 0.97, 0.9, 1)
|
||||
shadow_enabled = true
|
||||
directional_shadow_blend_splits = true
|
||||
directional_shadow_max_distance = 40.0
|
||||
|
||||
[node name="Environment" type="WorldEnvironment" parent="." unique_id=1374218548]
|
||||
environment = SubResource("Environment_gu74f")
|
||||
|
||||
[node name="MultiplayerSpawner" type="MultiplayerSpawner" parent="." unique_id=1020023513]
|
||||
spawn_path = NodePath("..")
|
||||
|
||||
[node name="LaunchDeck" type="StaticBody3D" parent="." unique_id=1144716368]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 5.8, -22.8)
|
||||
|
||||
[node name="Shape" type="CollisionShape3D" parent="LaunchDeck" unique_id=1332771168]
|
||||
shape = SubResource("BoxShape3D_qnd6n")
|
||||
|
||||
[node name="Mesh" type="MeshInstance3D" parent="LaunchDeck" unique_id=206374851]
|
||||
material_override = SubResource("StandardMaterial3D_op3e8")
|
||||
mesh = SubResource("BoxMesh_vid1e")
|
||||
|
||||
[node name="Zipline" parent="." unique_id=982324784 instance=ExtResource("6_i1eo7")]
|
||||
|
||||
[node name="CarryableBox1" parent="." unique_id=161398026 instance=ExtResource("7_carrybox")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 2.2, 0.55, -1.6)
|
||||
|
||||
[node name="CarryableBox2" parent="." unique_id=293502433 instance=ExtResource("7_carrybox")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 3, 0.55, -2.4)
|
||||
|
||||
[node name="World" parent="." unique_id=332495265 instance=ExtResource("7_gu74f")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -3.970745, 0)
|
||||
|
||||
[node name="RigidBody3D" type="RigidBody3D" parent="." unique_id=1008810927]
|
||||
transform = Transform3D(0.1, 0, 0, 0, 0.1, 0, 0, 0, 0.1, 0, 0, 0)
|
||||
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="RigidBody3D" unique_id=1173685673]
|
||||
shape = SubResource("BoxShape3D_cmo1i")
|
||||
|
||||
[node name="MeshInstance3D" type="MeshInstance3D" parent="RigidBody3D" unique_id=751871190]
|
||||
mesh = SubResource("BoxMesh_i1eo7")
|
||||
|
||||
[node name="Sky3D" type="WorldEnvironment" parent="." unique_id=1566118792]
|
||||
environment = SubResource("Environment_8l5dh")
|
||||
camera_attributes = SubResource("CameraAttributesPractical_6lvsd")
|
||||
script = ExtResource("12_d6btn")
|
||||
current_time = 9.188613333331746
|
||||
wind_direction = 2.35619451204804
|
||||
metadata/_custom_type_script = "uid://deo0gxkmk2ahd"
|
||||
|
||||
[node name="SunLight" type="DirectionalLight3D" parent="Sky3D" unique_id=1160740787]
|
||||
transform = Transform3D(0.69260204, -0.4109358, 0.59281886, 0, 0.82185286, 0.56969976, -0.7213199, -0.3945752, 0.56921697, 0.59281886, 0.56969976, 0.56921697)
|
||||
shadow_enabled = true
|
||||
|
||||
[node name="MoonLight" type="DirectionalLight3D" parent="Sky3D" unique_id=733450219]
|
||||
transform = Transform3D(0, -0.6365949, 0.77119833, 0.8476899, 0.4091145, 0.3377085, -0.53049195, 0.65373707, 0.5396351, 0.77119833, 0.3377085, 0.5396351)
|
||||
light_color = Color(0.572549, 0.776471, 0.956863, 1)
|
||||
light_energy = 0.0
|
||||
|
||||
[node name="SkyDome" type="Node" parent="Sky3D" unique_id=1881177694]
|
||||
script = ExtResource("13_wc2es")
|
||||
sun_azimuth = -2.3358865
|
||||
sun_altitude = -0.9646558
|
||||
moon_azimuth = -2.181345
|
||||
moon_altitude = -1.226315
|
||||
wind_direction = 2.35619451204804
|
||||
|
||||
[node name="TimeOfDay" type="Node" parent="Sky3D" unique_id=270602983]
|
||||
script = ExtResource("14_gk3u8")
|
||||
dome_path = NodePath("../SkyDome")
|
||||
current_time = 9.188613333331746
|
||||
|
||||
@@ -1,180 +0,0 @@
|
||||
@tool
|
||||
extends Node3D
|
||||
class_name Zipline
|
||||
## 滑索(Zipline):两点之间一条会下垂的钢缆。玩家走到缆下按 E 挂上,
|
||||
## 沿缆滑向较低的那一端,中途可按 S 刹车、按空格主动跳离。
|
||||
##
|
||||
## 场景结构(res://world/zipline/zipline.tscn):
|
||||
## Zipline (Node3D, 本脚本)
|
||||
## ├─ AnchorA (Marker3D) ← 高处起点,在视口里直接拖
|
||||
## └─ AnchorB (Marker3D) ← 低处终点
|
||||
## 缆绳网格是本脚本在 _ready() 里生成的一个 MeshInstance3D 子节点,
|
||||
## 故意不给 owner,所以不会被写进 .tscn —— 它完全由两个锚点推导出来,
|
||||
## 不存在「文件里的网格和锚点对不上」这种脏数据。
|
||||
##
|
||||
## 手感参数(速度/刹车/吸附半径)放在「缆」这一侧而不是玩家身上:
|
||||
## 同一张关卡里可以有快缆和慢缆,玩家只负责读参数,不负责定义手感。
|
||||
##
|
||||
## 曲线模型:两点连线再减去一个抛物线形下垂量
|
||||
## P(t) = lerp(A, B, t) - UP * sag * 4t(1-t)
|
||||
## 权重 4t(1-t) 在 t=0/1 处为 0、t=0.5 处为 1,所以锚点高度就是真实挂点高度、
|
||||
## sag 就是中点下垂深度 —— 调参所见即所得,不用去解悬链线方程。
|
||||
##
|
||||
## 约束:本节点不要旋转/缩放,靠拖动两个锚点摆位。
|
||||
|
||||
## 所有滑索都进这个组,玩家靠它找缆
|
||||
const GROUP: StringName = &"zipline"
|
||||
## 缆绳截面的边数。缆很细,6 边足够,还省顶点
|
||||
const RING_SEGMENTS: int = 6
|
||||
|
||||
@export_group("缆绳")
|
||||
## 中点下垂深度(米)。0 = 绷直。真实滑索是绷紧的,所以别调太大
|
||||
@export_range(0.0, 12.0, 0.05) var sag: float = 1.2
|
||||
## 沿缆采样段数。越高越圆滑,也越费顶点
|
||||
@export_range(8, 128, 1) var segments: int = 48
|
||||
## 缆绳半径(米)
|
||||
@export_range(0.01, 0.3, 0.005) var radius: float = 0.05
|
||||
## 缆绳材质。留空则用内置的深色金属
|
||||
@export var cable_material: Material
|
||||
|
||||
@export_group("乘坐")
|
||||
## 人挂在缆下方多深(米)。约等于「双手举过头顶抓住缆」的高度
|
||||
@export var hang_offset: float = 1.15
|
||||
## 玩家离缆多近能按 E 挂上(米)。给得宽一点:缆悬在头顶时也算「够得着」
|
||||
@export var attach_radius: float = 4.0
|
||||
## 沿缆最高速度(米/秒)
|
||||
@export var max_speed: float = 18.0
|
||||
## 重力沿缆方向的缩放。1 = 完全按坡度加速,调小则「黏」一些
|
||||
@export var gravity_scale: float = 1.0
|
||||
## 按住后退键时的刹车减速度(米/秒²)
|
||||
@export var brake_accel: float = 14.0
|
||||
## 剩余行程短于此值就不让挂(米)。否则刚挂上就到头,像被弹开
|
||||
@export var min_ride: float = 3.0
|
||||
|
||||
@onready var anchor_a: Marker3D = $AnchorA
|
||||
@onready var anchor_b: Marker3D = $AnchorB
|
||||
|
||||
## 缆绳网格节点,_ready() 里现造
|
||||
var _cable: MeshInstance3D = null
|
||||
## 缆绳总长(米)。生成网格时顺带算出来,玩家用它把速度换算成 t 的推进量
|
||||
var length: float = 0.0
|
||||
## 编辑器里用来比对「锚点/参数变了没有」,避免每帧重建网格
|
||||
var _editor_sig: String = ""
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
add_to_group(GROUP)
|
||||
_cable = MeshInstance3D.new()
|
||||
_cable.name = "Cable"
|
||||
add_child(_cable)
|
||||
_rebuild_cable()
|
||||
# 编辑器下开着 _process,拖动锚点能立刻看到缆绳跟着变;运行时不需要
|
||||
set_process(Engine.is_editor_hint())
|
||||
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
if anchor_a == null or anchor_b == null:
|
||||
return
|
||||
# 只在签名变化时重建 —— SurfaceTool 生成网格不便宜,不能每帧做
|
||||
var sig := "%s|%s|%.3f|%d|%.4f" % [anchor_a.position, anchor_b.position, sag, segments, radius]
|
||||
if sig == _editor_sig:
|
||||
return
|
||||
_editor_sig = sig
|
||||
_rebuild_cable()
|
||||
|
||||
|
||||
# ---------------- 对外接口(玩家控制器只读这几个) ----------------
|
||||
|
||||
## 缆上参数 t∈[0,1] 处的世界坐标。
|
||||
## 曲线先在**本地空间**算完再转世界:sag 是本地单位,父节点一有缩放整条缆要跟着一起缩,
|
||||
## 形状才不走样(混用 global 锚点 + 未缩放的 sag,下垂比例会随父节点缩放而变)。
|
||||
func sample(t: float) -> Vector3:
|
||||
return to_global(_curve_local(t))
|
||||
|
||||
|
||||
## 缆上参数 t 处的世界方向(单位向量,指向 B)。
|
||||
## 用解析导数而不是前后差分:差分在 t=0/1 处要越界取样,端点会偏。
|
||||
func tangent(t: float) -> Vector3:
|
||||
var d := (anchor_b.position - anchor_a.position) - Vector3.UP * (sag * 4.0 * (1.0 - 2.0 * t))
|
||||
return (global_transform.basis * d).normalized()
|
||||
|
||||
|
||||
## 乘坐时人应该待的世界坐标:缆点再往下挂 hang_offset。
|
||||
func ride_point(t: float) -> Vector3:
|
||||
return to_global(_curve_local(t) - Vector3.UP * hang_offset)
|
||||
|
||||
|
||||
## 找出世界坐标 pos 在缆上最近的一点,供玩家「就近吸附」。
|
||||
## 返回 { t, point, distance, dir, remaining }:
|
||||
## dir +1 表示往 B 滑、-1 表示往 A 滑(永远是往低的那端)
|
||||
## remaining 沿 dir 方向还剩多少米
|
||||
func closest_point(pos: Vector3) -> Dictionary:
|
||||
var best_t := 0.0
|
||||
var best_d := INF
|
||||
for i in range(segments + 1):
|
||||
var t := float(i) / float(segments)
|
||||
var d := sample(t).distance_squared_to(pos)
|
||||
if d < best_d:
|
||||
best_d = d
|
||||
best_t = t
|
||||
var dir := _downhill_dir()
|
||||
return {
|
||||
"t": best_t,
|
||||
"point": sample(best_t),
|
||||
"distance": sqrt(best_d),
|
||||
"dir": dir,
|
||||
"remaining": absf((1.0 if dir > 0 else 0.0) - best_t) * length,
|
||||
}
|
||||
|
||||
|
||||
# ---------------- 内部 ----------------
|
||||
|
||||
## 低的那一端才是出口 —— 滑索只会往下滑。两端等高时退回 A→B。
|
||||
func _downhill_dir() -> int:
|
||||
return 1 if anchor_b.global_position.y <= anchor_a.global_position.y else -1
|
||||
|
||||
|
||||
## 下垂曲线在**本节点局部空间**的点。sag 是本地单位,所以父节点缩放时整条缆一起缩、形状不变。
|
||||
func _curve_local(t: float) -> Vector3:
|
||||
return anchor_a.position.lerp(anchor_b.position, t) - Vector3.UP * (sag * 4.0 * t * (1.0 - t))
|
||||
|
||||
|
||||
## 把下垂曲线扫成一根管子。用三角形带:每个截面 RING_SEGMENTS+1 个顶点,
|
||||
## 多出来的那个和首顶点重合,接缝处法线才连续、不会裂开一条缝。
|
||||
func _rebuild_cable() -> void:
|
||||
if _cable == null or anchor_a == null or anchor_b == null:
|
||||
return
|
||||
var st := SurfaceTool.new()
|
||||
st.begin(Mesh.PRIMITIVE_TRIANGLE_STRIP)
|
||||
var prev := _curve_local(0.0)
|
||||
length = 0.0
|
||||
for i in range(segments + 1):
|
||||
var t := float(i) / float(segments)
|
||||
var p := _curve_local(t)
|
||||
if i > 0:
|
||||
length += p.distance_to(prev)
|
||||
prev = p
|
||||
# 截面参考轴取「切线 × 世界上方向」。切线接近竖直时叉乘会退化成零向量,
|
||||
# 这时换 RIGHT 兜底,否则 normalize() 会吐 NaN 把整根缆搅烂。
|
||||
var tan_dir := (_curve_local(minf(t + 0.01, 1.0)) - _curve_local(maxf(t - 0.01, 0.0))).normalized()
|
||||
var n := tan_dir.cross(Vector3.UP)
|
||||
if n.length_squared() < 0.0001:
|
||||
n = tan_dir.cross(Vector3.RIGHT)
|
||||
n = n.normalized()
|
||||
var binormal := n.cross(tan_dir).normalized()
|
||||
for j in range(RING_SEGMENTS + 1):
|
||||
var ang := TAU * float(j) / float(RING_SEGMENTS)
|
||||
var off := n * cos(ang) + binormal * sin(ang)
|
||||
st.set_normal(off)
|
||||
st.add_vertex(p + off * radius)
|
||||
_cable.mesh = st.commit()
|
||||
var mat: Material = cable_material
|
||||
if mat == null:
|
||||
var std := StandardMaterial3D.new()
|
||||
std.albedo_color = Color(0.14, 0.15, 0.18)
|
||||
std.metallic = 0.85
|
||||
std.roughness = 0.3
|
||||
# 三角形带的绕序不保证,直接双面,省得算错朝向
|
||||
std.cull_mode = BaseMaterial3D.CULL_DISABLED
|
||||
mat = std
|
||||
_cable.material_override = mat
|
||||
@@ -1 +0,0 @@
|
||||
uid://ddh4pqdknx8yf
|
||||
@@ -1,12 +0,0 @@
|
||||
[gd_scene format=3 uid="uid://12qonfnowpas"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://ddh4pqdknx8yf" path="res://world/zipline/zipline.gd" id="1_pj3rq"]
|
||||
|
||||
[node name="Zipline" type="Node3D" unique_id=982324784]
|
||||
script = ExtResource("1_pj3rq")
|
||||
|
||||
[node name="AnchorA" type="Marker3D" parent="." unique_id=267373356]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 8, -24)
|
||||
|
||||
[node name="AnchorB" type="Marker3D" parent="." unique_id=1805183028]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 2, 18)
|
||||