added terrain3d
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2024 Cory Petkovsek, Roope Palmroos, and Contributors.
|
Copyright (c) 2025 Cory Petkovsek, Roope Palmroos, and Contributors.
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
@@ -3,34 +3,26 @@
|
|||||||
# Terrain3D
|
# Terrain3D
|
||||||
A high performance, editable terrain system for Godot 4.
|
A high performance, editable terrain system for Godot 4.
|
||||||
|
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
* Written in C++ as a GDExtension addon, which works with official engine builds
|
* Written in C++ as a GDExtension addon, which works with official builds of Godot Engine
|
||||||
* Can be accessed by GDScript, C#, and any language Godot supports
|
* Can be accessed by GDScript, C#, and any language Godot supports
|
||||||
* Geometric Clipmap Mesh Terrain, as used in The Witcher 3. See [System Architecture](https://terrain3d.readthedocs.io/en/stable/docs/system_architecture.html)
|
* Geomorphing Geometric Clipmap Mesh Terrain, as used in The Witcher 3. See [System Architecture](https://terrain3d.readthedocs.io/en/stable/docs/system_architecture.html)
|
||||||
* Terrains as small as 64x64m up to 65.5x65.5km (4295km^2) in variable sized regions
|
* Terrains as small as 64x64m up to 65.5x65.5km (4295km^2) in variable sized regions
|
||||||
* Up to 32 textures
|
* Up to 32 textures
|
||||||
* Up to 10 levels of detail
|
* Up to 10 levels of detail for the terrain mesh
|
||||||
* Foliage instancing
|
* Foliage instancing, with up to 10 levels of detail, and a shadow impostor
|
||||||
* Sculpting, holes, texture painting, texture detiling, painting colors and wetness
|
* Sculpting, holes, texture painting, texture detiling, painting colors and wetness
|
||||||
* Imports heightmaps from [HTerrain](https://github.com/Zylann/godot_heightmap_plugin/), WorldMachine, Unity, Unreal and any tool that can export a heightmap (raw/r16/exr/+). See [importing data](https://terrain3d.readthedocs.io/en/stable/docs/import_export.html)
|
* Imports heightmaps from [HTerrain](https://github.com/Zylann/godot_heightmap_plugin/), Gaea, World Creator, World Machine, Unity, Unreal and any tool that can export a heightmap. See [heightmaps](https://terrain3d.readthedocs.io/en/stable/docs/heightmaps.html)
|
||||||
|
|
||||||
See [Project Status](https://terrain3d.readthedocs.io/en/stable/docs/project_status.html) for details.
|
|
||||||
|
|
||||||
## Getting Started
|
## Getting Started
|
||||||
|
|
||||||
1. Read the [Installation & Upgrades](https://terrain3d.readthedocs.io/en/stable/docs/installation.html) instructions.
|
1. Read the [Installation & Upgrades](https://terrain3d.readthedocs.io/en/stable/docs/installation.html) instructions.
|
||||||
|
|
||||||
2. For support, read [Getting Help](https://terrain3d.readthedocs.io/en/stable/docs/getting_help.html) or join our [Discord server](https://tokisan.com/discord).
|
2. For support, read [Getting Help](https://terrain3d.readthedocs.io/en/stable/docs/getting_help.html) and join our [Discord server](https://tokisan.com/discord).
|
||||||
|
|
||||||
3. Watch the tutorial videos:
|
3. Watch the [tutorial videos](https://terrain3d.readthedocs.io/en/stable/docs/tutorial_videos.html).
|
||||||
|
|
||||||
**Installation, Setup, Basic Usage**
|
|
||||||
|
|
||||||
[](https://youtu.be/oV8c9alXVwU)
|
|
||||||
|
|
||||||
**Texture Painting, Holes, Navigation, Advanced Usage**
|
|
||||||
|
|
||||||
[](https://youtu.be/YtiAI2F6Xkk)
|
|
||||||
|
|
||||||
|
|
||||||
## Credit
|
## Credit
|
||||||
@@ -41,9 +33,8 @@ 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)|
|
| **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)|
|
||||||
| **Roope Palmroos, Outobugi 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/outobugi) [<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/outobugi) [<img src="https://github.com/dmhendricks/signature-social-icons/blob/master/icons/round-flat-filled/35px/www.png?raw=true" width="24"/>](https://outobugi.com/) [<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/@outobugi)|
|
| **Roope Palmroos, Outobugi 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/outobugi) [<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/outobugi) [<img src="https://github.com/dmhendricks/signature-social-icons/blob/master/icons/round-flat-filled/35px/www.png?raw=true" width="24"/>](https://outobugi.com/) [<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/@outobugi)|
|
||||||
|
|
||||||
And other contributors displayed on the right of the github page and in [AUTHORS.md](https://github.com/TokisanGames/Terrain3D/blob/main/AUTHORS.md).
|
And other contributors displayed on the right of the github page and in [AUTHORS.md](https://terrain3d.readthedocs.io/en/stable/docs/authors.html).
|
||||||
|
|
||||||
Geometry clipmap mesh code created by [Mike J. Savage](https://mikejsavage.co.uk/blog/geometry-clipmaps.html). Blog and repository code released under the MIT license per email communication with Mike.
|
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
addons/terrain_3d/bin/libterrain.web.debug.wasm32.wasm
Normal file
BIN
addons/terrain_3d/bin/libterrain.web.debug.wasm32.wasm
Normal file
Binary file not shown.
BIN
addons/terrain_3d/bin/libterrain.web.release.wasm32.wasm
Normal file
BIN
addons/terrain_3d/bin/libterrain.web.release.wasm32.wasm
Normal file
Binary file not shown.
Binary file not shown.
@@ -1 +0,0 @@
|
|||||||
uid://cuhwgfm8bkknk
|
|
||||||
67
addons/terrain_3d/extras/hex_grid.gdshaderinc
Normal file
67
addons/terrain_3d/extras/hex_grid.gdshaderinc
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
// Copyright © 2025 Cory Petkovsek, Roope Palmroos, and Contributors.
|
||||||
|
// This shader snippet draws a hex grid
|
||||||
|
|
||||||
|
// To use it, add this line to the top of your shader:
|
||||||
|
// #include "res://addons/terrain_3d/extras/hex_grid.gdshaderinc"
|
||||||
|
|
||||||
|
// And this line at the bottom of your shader:
|
||||||
|
// draw_hex_grid(uv2, _region_texel_size, w_normal, ALBEDO);
|
||||||
|
|
||||||
|
mat2 rotate2d(float _angle) {
|
||||||
|
return mat2(vec2(cos(_angle),-sin(_angle)), vec2(sin(_angle), cos(_angle)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw_hex_grid(vec2 uv, float texel_size, vec3 normal, inout vec3 albedo) {
|
||||||
|
float hex_size = 0.02;
|
||||||
|
float line_thickness = 0.04;
|
||||||
|
|
||||||
|
vec2 guv = (uv - vec2(0.5 * texel_size)) / hex_size;
|
||||||
|
|
||||||
|
// Convert UV to axial hex coordinates
|
||||||
|
float q = (sqrt(3.0) / 3.0 * guv.x - 1.0 / 3.0 * guv.y);
|
||||||
|
float r = (2.0 / 3.0 * guv.y);
|
||||||
|
|
||||||
|
// Cube coordinates for the hex (q, r, -q-r)
|
||||||
|
float x = q;
|
||||||
|
float z = r;
|
||||||
|
float y = -x - z;
|
||||||
|
|
||||||
|
// Round to the nearest hex center
|
||||||
|
vec3 rounded = round(vec3(x, y, z));
|
||||||
|
vec3 diff = abs(vec3(x, y, z) - rounded);
|
||||||
|
|
||||||
|
// Fix rounding errors
|
||||||
|
if (diff.x > diff.y && diff.x > diff.z) {
|
||||||
|
rounded.x = -rounded.y - rounded.z;
|
||||||
|
} else if (diff.y > diff.z) {
|
||||||
|
rounded.y = -rounded.x - rounded.z;
|
||||||
|
} else {
|
||||||
|
rounded.z = -rounded.x - rounded.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the hex center in UV space
|
||||||
|
vec2 hex_center = vec2(
|
||||||
|
sqrt(3.0) * rounded.x + sqrt(3.0) / 2.0 * rounded.z,
|
||||||
|
3.0 / 2.0 * rounded.z
|
||||||
|
);
|
||||||
|
|
||||||
|
// Relative position within the hex
|
||||||
|
vec2 local_pos = guv - hex_center;
|
||||||
|
vec2 lines_uv = local_pos;
|
||||||
|
float line = 1.0;
|
||||||
|
|
||||||
|
for (int i = 0; i < 6; i++) {
|
||||||
|
vec2 luv = lines_uv * rotate2d(radians(60.0 * float(i) + 30.0));
|
||||||
|
float dist = abs(dot(luv + vec2(0.90), vec2(0.0, 1.0)));
|
||||||
|
line = min(line, dist);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter lines by slope
|
||||||
|
float slope = 4.; // Can also assign to (auto_slope * 4.) to match grass placement
|
||||||
|
float slope_factor = clamp(dot(vec3(0., 1., 0.), slope * (normal - 1.) + 1.), 0., 1.);
|
||||||
|
|
||||||
|
// Draw hex grid
|
||||||
|
albedo = mix(albedo, vec3(1.0), smoothstep(line_thickness + 0.02, line_thickness, line) * slope_factor);
|
||||||
|
// Draw Hex center dot
|
||||||
|
albedo = mix(albedo, vec3(0.0, 0.5, 0.5), smoothstep(0.11, 0.10, length(local_pos)) * slope_factor);
|
||||||
|
}
|
||||||
1
addons/terrain_3d/extras/hex_grid.gdshaderinc.uid
Normal file
1
addons/terrain_3d/extras/hex_grid.gdshaderinc.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://mri8pfoj2mfk
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
## Import From SimpleGrassTextured
|
# Copyright © 2025 Cory Petkovsek, Roope Palmroos, and Contributors.
|
||||||
|
# Import From SimpleGrassTextured
|
||||||
#
|
#
|
||||||
# This script demonstrates how to import transforms from SimpleGrassTextured. To use it:
|
# This script demonstrates how to import transforms from SimpleGrassTextured. To use it:
|
||||||
#
|
#
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
uid://bm8g6gkvrerqy
|
uid://bllcuwetve45k
|
||||||
|
|||||||
400
addons/terrain_3d/extras/lightweight.gdshader
Normal file
400
addons/terrain_3d/extras/lightweight.gdshader
Normal file
@@ -0,0 +1,400 @@
|
|||||||
|
shader_type spatial;
|
||||||
|
render_mode blend_mix,depth_draw_opaque,cull_back,diffuse_burley,specular_schlick_ggx,skip_vertex_transform;
|
||||||
|
|
||||||
|
/* This is an example stripped down shader with maximum performance in mind.
|
||||||
|
* Only Autoshader/Base/Over/Blend/Holes/Colormap are supported.
|
||||||
|
* All terrain normal calculations take place in vetex() as well as control map reads
|
||||||
|
* for the bilinear blend, when not skippable have moved to vertex() too.
|
||||||
|
*
|
||||||
|
* A single controlmap lookup in fragment is added at distances where the vertices spread too wide.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Defined Constants
|
||||||
|
#define SKIP_PASS 0
|
||||||
|
#define VERTEX_PASS 1
|
||||||
|
#define FRAGMENT_PASS 2
|
||||||
|
|
||||||
|
#if CURRENT_RENDERER == RENDERER_COMPATIBILITY
|
||||||
|
#define fma(a, b, c) ((a) * (b) + (c))
|
||||||
|
#define dFdxCoarse(a) dFdx(a)
|
||||||
|
#define dFdyCoarse(a) dFdy(a)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Private uniforms
|
||||||
|
uniform vec3 _camera_pos = vec3(0.f);
|
||||||
|
uniform float _mesh_size = 48.f;
|
||||||
|
uniform uint _background_mode = 1u; // NONE = 0, FLAT = 1, NOISE = 2
|
||||||
|
uniform uint _mouse_layer = 0x80000000u; // Layer 32
|
||||||
|
uniform float _vertex_spacing = 1.0;
|
||||||
|
uniform float _vertex_density = 1.0; // = 1/_vertex_spacing
|
||||||
|
uniform float _region_size = 1024.0;
|
||||||
|
uniform float _region_texel_size = 0.0009765625; // = 1/1024
|
||||||
|
uniform int _region_map_size = 32;
|
||||||
|
uniform int _region_map[1024];
|
||||||
|
uniform vec2 _region_locations[1024];
|
||||||
|
uniform float _texture_normal_depth_array[32];
|
||||||
|
uniform float _texture_ao_strength_array[32];
|
||||||
|
uniform float _texture_roughness_mod_array[32];
|
||||||
|
uniform float _texture_uv_scale_array[32];
|
||||||
|
uniform vec4 _texture_color_array[32];
|
||||||
|
uniform highp sampler2DArray _height_maps : repeat_disable;
|
||||||
|
uniform highp sampler2DArray _control_maps : repeat_disable;
|
||||||
|
uniform highp sampler2DArray _color_maps : source_color, filter_linear_mipmap, repeat_disable;
|
||||||
|
uniform highp sampler2DArray _texture_array_albedo : source_color, filter_linear_mipmap, repeat_enable;
|
||||||
|
uniform highp sampler2DArray _texture_array_normal : hint_normal, filter_linear_mipmap, repeat_enable;
|
||||||
|
|
||||||
|
|
||||||
|
// Public uniforms
|
||||||
|
uniform float auto_slope : hint_range(0, 10) = 1.0;
|
||||||
|
uniform float auto_height_reduction : hint_range(0, 1) = 0.1;
|
||||||
|
uniform int auto_base_texture : hint_range(0, 31) = 0;
|
||||||
|
uniform int auto_overlay_texture : hint_range(0, 31) = 1;
|
||||||
|
|
||||||
|
uniform bool height_blending = true;
|
||||||
|
uniform bool world_space_normal_blend = true;
|
||||||
|
uniform float blend_sharpness : hint_range(0, 1) = 0.87;
|
||||||
|
|
||||||
|
// Varyings & Types
|
||||||
|
|
||||||
|
struct Material {
|
||||||
|
vec4 alb_ht;
|
||||||
|
vec4 nrm_rg;
|
||||||
|
int base;
|
||||||
|
int over;
|
||||||
|
float blend;
|
||||||
|
float nrm_depth;
|
||||||
|
float ao_str;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
varying vec3 v_vertex;
|
||||||
|
varying vec3 v_normal;
|
||||||
|
varying flat uint v_control[4];
|
||||||
|
varying flat int v_lerp;
|
||||||
|
varying mat3 v_tbn;
|
||||||
|
|
||||||
|
////////////////////////
|
||||||
|
// Vertex
|
||||||
|
////////////////////////
|
||||||
|
|
||||||
|
// Takes in world space XZ (UV) coordinates & search depth (only applicable for background mode none)
|
||||||
|
// Returns ivec3 with:
|
||||||
|
// XY: (0 to _region_size - 1) coordinates within a region
|
||||||
|
// Z: layer index used for texturearrays, -1 if not in a region
|
||||||
|
ivec3 get_index_coord(const vec2 uv, const int search) {
|
||||||
|
vec2 r_uv = round(uv);
|
||||||
|
vec2 o_uv = mod(r_uv,_region_size);
|
||||||
|
ivec2 pos;
|
||||||
|
int bounds, layer_index = -1;
|
||||||
|
for (int i = -1; i < clamp(search, SKIP_PASS, FRAGMENT_PASS); i++) {
|
||||||
|
if ((layer_index == -1 && _background_mode == 0u ) || i < 0) {
|
||||||
|
r_uv -= i == -1 ? vec2(0.0) : vec2(float(o_uv.x <= o_uv.y), float(o_uv.y <= o_uv.x));
|
||||||
|
pos = ivec2(floor((r_uv) * _region_texel_size)) + (_region_map_size / 2);
|
||||||
|
bounds = int(uint(pos.x | pos.y) < uint(_region_map_size));
|
||||||
|
layer_index = (_region_map[ pos.y * _region_map_size + pos.x ] * bounds - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ivec3(ivec2(mod(r_uv,_region_size)), layer_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Takes in descaled (world_space / region_size) world to region space XZ (UV2) coordinates, returns vec3 with:
|
||||||
|
// XY: (0. to 1.) coordinates within a region
|
||||||
|
// Z: layer index used for texturearrays, -1 if not in a region
|
||||||
|
vec3 get_index_uv(const vec2 uv2) {
|
||||||
|
ivec2 pos = ivec2(floor(uv2)) + (_region_map_size / 2);
|
||||||
|
int bounds = int(uint(pos.x | pos.y) < uint(_region_map_size));
|
||||||
|
int layer_index = _region_map[ pos.y * _region_map_size + pos.x ] * bounds - 1;
|
||||||
|
return vec3(uv2 - _region_locations[layer_index], float(layer_index));
|
||||||
|
}
|
||||||
|
|
||||||
|
void vertex() {
|
||||||
|
// Get vertex of flat plane in world coordinates and set world UV
|
||||||
|
v_vertex = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;
|
||||||
|
|
||||||
|
// Camera distance to vertex on flat plane
|
||||||
|
float v_vertex_xz_dist = length(v_vertex.xz - _camera_pos.xz);
|
||||||
|
|
||||||
|
// Geomorph vertex, set end and start for linear height interpolate
|
||||||
|
float scale = MODEL_MATRIX[0][0];
|
||||||
|
float vertex_lerp = smoothstep(0.55, 0.95, (v_vertex_xz_dist / scale - _mesh_size - 4.0) / (_mesh_size - 2.0));
|
||||||
|
vec2 v_fract = fract(VERTEX.xz * 0.5) * 2.0;
|
||||||
|
// For LOD0 morph from a regular grid to an alternating grid to align with LOD1+
|
||||||
|
vec2 shift = (scale < _vertex_spacing + 1e-6) ? // LOD0 or not
|
||||||
|
// Shift from regular to symetric
|
||||||
|
mix(v_fract, vec2(v_fract.x, -v_fract.y),
|
||||||
|
round(fract(round(mod(v_vertex.z * _vertex_density, 4.0)) *
|
||||||
|
round(mod(v_vertex.x * _vertex_density, 4.0)) * 0.25))
|
||||||
|
) :
|
||||||
|
// Symetric shift
|
||||||
|
v_fract * round((fract(v_vertex.xz * 0.25 / scale) - 0.5) * 4.0);
|
||||||
|
vec2 start_pos = v_vertex.xz * _vertex_density;
|
||||||
|
vec2 end_pos = (v_vertex.xz - shift * scale) * _vertex_density;
|
||||||
|
v_vertex.xz -= shift * scale * vertex_lerp;
|
||||||
|
|
||||||
|
// UV coordinates in world space. Values are 0 to _region_size within regions
|
||||||
|
UV = v_vertex.xz * _vertex_density;
|
||||||
|
|
||||||
|
// UV coordinates in region space + texel offset. Values are 0 to 1 within regions
|
||||||
|
UV2 = fma(UV, vec2(_region_texel_size), vec2(0.5 * _region_texel_size));
|
||||||
|
|
||||||
|
const vec3 offsets = vec3(0, 1, 2);
|
||||||
|
ivec3 indexUV[4];
|
||||||
|
// control map lookups in vertex, used for bilinear blend in fragment.
|
||||||
|
indexUV[0] = get_index_coord(start_pos + offsets.xy, VERTEX_PASS);
|
||||||
|
indexUV[1] = get_index_coord(start_pos + offsets.yy, VERTEX_PASS);
|
||||||
|
indexUV[2] = get_index_coord(start_pos + offsets.yx, VERTEX_PASS);
|
||||||
|
indexUV[3] = get_index_coord(start_pos + offsets.xx, VERTEX_PASS);
|
||||||
|
// Mask off Scale/Rotation/Navigation bits to 0, as they are not used.
|
||||||
|
#define CONTROL_MASK 0xFFFFC07Du
|
||||||
|
v_control[0] = floatBitsToUint(texelFetch(_control_maps, indexUV[0], 0)).r & CONTROL_MASK;
|
||||||
|
v_control[1] = floatBitsToUint(texelFetch(_control_maps, indexUV[1], 0)).r & CONTROL_MASK;
|
||||||
|
v_control[2] = floatBitsToUint(texelFetch(_control_maps, indexUV[2], 0)).r & CONTROL_MASK;
|
||||||
|
v_control[3] = floatBitsToUint(texelFetch(_control_maps, indexUV[3], 0)).r & CONTROL_MASK;
|
||||||
|
bool full_auto = !bool((v_control[0] & v_control[1] & v_control[2] & v_control[3]) & 0x1u);
|
||||||
|
bool identical = !(
|
||||||
|
(v_control[0] == v_control[1]) &&
|
||||||
|
(v_control[1] == v_control[2]) &&
|
||||||
|
(v_control[2] == v_control[3]));
|
||||||
|
// Verticies are close enough, full auto shader, or all 4 indicies match, skip bilinear blend in fragment.
|
||||||
|
v_lerp = scale < _vertex_spacing + 1e-3 && vertex_lerp < 1e-3 && (full_auto || identical) ? 1 : 0;
|
||||||
|
|
||||||
|
// Discard vertices for Holes. 1 lookup
|
||||||
|
bool hole = bool(v_control[3] >>2u & 0x1u);
|
||||||
|
|
||||||
|
// Show holes to all cameras except mouse camera (on exactly 1 layer)
|
||||||
|
if ( !(CAMERA_VISIBLE_LAYERS == _mouse_layer) &&
|
||||||
|
(hole || (_background_mode == 0u && indexUV[3].z == -1))) {
|
||||||
|
v_vertex.x = 0. / 0.;
|
||||||
|
} else {
|
||||||
|
// Set final vertex height & calculate vertex normals. 3 lookups
|
||||||
|
ivec3 uv_a = get_index_coord(start_pos, VERTEX_PASS);
|
||||||
|
ivec3 uv_b = get_index_coord(end_pos, VERTEX_PASS);
|
||||||
|
float h = mix(texelFetch(_height_maps, uv_a, 0).r,texelFetch(_height_maps, uv_b, 0).r,vertex_lerp);
|
||||||
|
float u = mix(texelFetch(_height_maps, get_index_coord(start_pos + vec2(1,0), VERTEX_PASS), 0).r,
|
||||||
|
texelFetch(_height_maps, get_index_coord(end_pos + vec2(1,0), VERTEX_PASS), 0).r, vertex_lerp);
|
||||||
|
float v = mix(texelFetch(_height_maps, get_index_coord(start_pos + vec2(0,1), VERTEX_PASS), 0).r,
|
||||||
|
texelFetch(_height_maps, get_index_coord(end_pos + vec2(0,1), VERTEX_PASS), 0).r, vertex_lerp);
|
||||||
|
v_vertex.y = h;
|
||||||
|
v_normal = vec3(h - u, _vertex_spacing, h - v);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert model space to view space w/ skip_vertex_transform render mode
|
||||||
|
VERTEX = (VIEW_MATRIX * vec4(v_vertex, 1.0)).xyz;
|
||||||
|
|
||||||
|
// Apply terrain normals
|
||||||
|
vec3 w_normal = normalize(v_normal);
|
||||||
|
vec3 w_tangent = normalize(cross(w_normal, vec3(0.0, 0.0, 1.0)));
|
||||||
|
vec3 w_binormal = normalize(cross(w_normal, w_tangent));
|
||||||
|
|
||||||
|
v_tbn = mat3(w_tangent, w_normal, w_binormal);
|
||||||
|
|
||||||
|
NORMAL = normalize((VIEW_MATRIX * vec4(w_normal, 0.0)).xyz);
|
||||||
|
BINORMAL = normalize((VIEW_MATRIX * vec4(w_binormal, 0.0)).xyz);
|
||||||
|
TANGENT = normalize((VIEW_MATRIX * vec4(w_tangent, 0.0)).xyz);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////
|
||||||
|
// Fragment
|
||||||
|
////////////////////////
|
||||||
|
|
||||||
|
vec3 unpack_normal(vec4 rgba) {
|
||||||
|
return fma(rgba.xzy, vec3(2.0), vec3(-1.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 pack_normal(vec3 n) {
|
||||||
|
return fma(normalize(n.xzy), vec3(0.5), vec3(0.5));
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 height_blend4(vec4 a_value, float a_height, vec4 b_value, float b_height, float blend) {
|
||||||
|
if(height_blending) {
|
||||||
|
float ma = max(a_height + (1.0 - blend), b_height + blend) - (1.001 - blend_sharpness);
|
||||||
|
float b1 = max(a_height + (1.0 - blend) - ma, 0.0);
|
||||||
|
float b2 = max(b_height + blend - ma, 0.0);
|
||||||
|
return (a_value * b1 + b_value * b2) / (b1 + b2);
|
||||||
|
} else {
|
||||||
|
float contrast = 1.0 - blend_sharpness;
|
||||||
|
float factor = (blend - contrast) / contrast;
|
||||||
|
return mix(a_value, b_value, clamp(factor, 0.0, 1.0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float height_blend1(float a_value, float a_height, float b_value, float b_height, float blend) {
|
||||||
|
if(height_blending) {
|
||||||
|
float ma = max(a_height + (1.0 - blend), b_height + blend) - (1.001 - blend_sharpness);
|
||||||
|
float b1 = max(a_height + (1.0 - blend) - ma, 0.0);
|
||||||
|
float b2 = max(b_height + blend - ma, 0.0);
|
||||||
|
return (a_value * b1 + b_value * b2) / (b1 + b2);
|
||||||
|
} else {
|
||||||
|
float contrast = 1.0 - blend_sharpness;
|
||||||
|
float factor = (blend - contrast) / contrast;
|
||||||
|
return mix(a_value, b_value, clamp(factor, 0.0, 1.0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2-4 lookups ( 2-6 with dual scaling )
|
||||||
|
void get_material(vec4 ddxy, uint control, vec3 iuv_center, out Material out_mat) {
|
||||||
|
out_mat = Material(vec4(0.), vec4(0.), 0, 0, 0.0, 0.0, 0.0);
|
||||||
|
int region = int(iuv_center.z);
|
||||||
|
vec2 base_uv = v_vertex.xz * 0.5;
|
||||||
|
ddxy *= 0.5;
|
||||||
|
|
||||||
|
// Enable Autoshader if outside regions or painted in regions, otherwise manual painted
|
||||||
|
bool auto_shader = region < 0 || bool(control & 0x1u);
|
||||||
|
out_mat.base = int(auto_shader) * auto_base_texture + int(!auto_shader) * int(control >>27u & 0x1Fu);
|
||||||
|
out_mat.over = int(auto_shader) * auto_overlay_texture + int(!auto_shader) * int(control >> 22u & 0x1Fu);
|
||||||
|
out_mat.blend = float(auto_shader) * clamp(
|
||||||
|
(auto_slope * 2. * ( v_tbn[1].y - 1.) + 1.)
|
||||||
|
- auto_height_reduction * .01 * v_vertex.y // Reduce as vertices get higher
|
||||||
|
, 0., 1.) +
|
||||||
|
float(!auto_shader) * float(control >>14u & 0xFFu) * 0.003921568627450; // 1./255.0
|
||||||
|
|
||||||
|
out_mat.nrm_depth = _texture_normal_depth_array[out_mat.base];
|
||||||
|
out_mat.ao_str = _texture_ao_strength_array[out_mat.base];
|
||||||
|
|
||||||
|
vec2 matUV = base_uv;
|
||||||
|
vec4 albedo_ht = vec4(0.);
|
||||||
|
vec4 normal_rg = vec4(0.5, 0.5, 1.0, 1.0);
|
||||||
|
vec4 albedo_far = vec4(0.);
|
||||||
|
vec4 normal_far = vec4(0.5, 0.5, 1.0, 1.0);
|
||||||
|
float mat_scale = _texture_uv_scale_array[out_mat.base];
|
||||||
|
vec4 base_dd = ddxy;
|
||||||
|
|
||||||
|
if (out_mat.blend < 1.0) {
|
||||||
|
// 2 lookups
|
||||||
|
//each time we change scale, recalculate antitiling from baseline to maintain continuity.
|
||||||
|
matUV = base_uv * mat_scale;
|
||||||
|
base_dd *= mat_scale;
|
||||||
|
albedo_ht = textureGrad(_texture_array_albedo, vec3(matUV, float(out_mat.base)), base_dd.xy, base_dd.zw);
|
||||||
|
normal_rg = textureGrad(_texture_array_normal, vec3(matUV, float(out_mat.base)), base_dd.xy, base_dd.zw);
|
||||||
|
|
||||||
|
// Unpack & rotate base normal for blending
|
||||||
|
normal_rg.xyz = unpack_normal(normal_rg);
|
||||||
|
}
|
||||||
|
// Apply color to base
|
||||||
|
albedo_ht.rgb *= _texture_color_array[out_mat.base].rgb;
|
||||||
|
|
||||||
|
// Apply Roughness modifier to base
|
||||||
|
normal_rg.a = clamp(normal_rg.a + _texture_roughness_mod_array[out_mat.base], 0., 1.);
|
||||||
|
|
||||||
|
out_mat.alb_ht = albedo_ht;
|
||||||
|
out_mat.nrm_rg = normal_rg;
|
||||||
|
|
||||||
|
if (out_mat.blend > 0.) {
|
||||||
|
// 2 lookups
|
||||||
|
// Setup overlay texture to blend
|
||||||
|
float mat_scale2 = _texture_uv_scale_array[out_mat.over];
|
||||||
|
vec2 matUV2 = base_uv * mat_scale2;
|
||||||
|
vec4 over_dd = ddxy * mat_scale2;
|
||||||
|
vec4 albedo_ht2 = textureGrad(_texture_array_albedo, vec3(matUV2, float(out_mat.over)), over_dd.xy, over_dd.zw);
|
||||||
|
vec4 normal_rg2 = textureGrad(_texture_array_normal, vec3(matUV2, float(out_mat.over)), over_dd.xy, over_dd.zw);
|
||||||
|
|
||||||
|
// Unpack & rotate overlay normal for blending
|
||||||
|
normal_rg2.xyz = unpack_normal(normal_rg2);
|
||||||
|
|
||||||
|
// Apply color to overlay
|
||||||
|
albedo_ht2.rgb *= _texture_color_array[out_mat.over].rgb;
|
||||||
|
|
||||||
|
// Apply Roughness modifier to overlay
|
||||||
|
normal_rg2.a = clamp(normal_rg2.a + _texture_roughness_mod_array[out_mat.over], 0., 1.);
|
||||||
|
|
||||||
|
// apply world space normal weighting from base, to overlay layer
|
||||||
|
// Its a matrix Mult, but the value is rather high, so not cutting this one.
|
||||||
|
if (world_space_normal_blend) {
|
||||||
|
albedo_ht2.a *= bool(control >>3u & 0x1u) ? 1.0 : clamp((v_tbn * normal_rg.xyz).y, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Blend overlay and base
|
||||||
|
out_mat.alb_ht = height_blend4(albedo_ht, albedo_ht.a, albedo_ht2, albedo_ht2.a, out_mat.blend);
|
||||||
|
out_mat.nrm_rg = height_blend4(normal_rg, albedo_ht.a, normal_rg2, albedo_ht2.a, out_mat.blend);
|
||||||
|
out_mat.nrm_depth = height_blend1(_texture_normal_depth_array[out_mat.base], albedo_ht.a,
|
||||||
|
_texture_normal_depth_array[out_mat.over], albedo_ht2.a, out_mat.blend);
|
||||||
|
out_mat.ao_str = height_blend1(_texture_ao_strength_array[out_mat.base], albedo_ht.a,
|
||||||
|
_texture_ao_strength_array[out_mat.over], albedo_ht2.a, out_mat.blend);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fragment() {
|
||||||
|
// Recover UVs
|
||||||
|
vec2 uv = UV;
|
||||||
|
vec2 uv2 = UV2;
|
||||||
|
|
||||||
|
vec3 base_ddx = dFdxCoarse(v_vertex);
|
||||||
|
vec3 base_ddy = dFdyCoarse(v_vertex);
|
||||||
|
vec4 base_derivatives = vec4(base_ddx.xz, base_ddy.xz);
|
||||||
|
float region_mip = log2(max(length(base_ddx.xz), length(base_ddy.xz)) * _vertex_density);
|
||||||
|
|
||||||
|
// Colormap. 1 lookup
|
||||||
|
// For speed sake, we'll live with cross region artifacts.
|
||||||
|
#define COLOR_MAP vec4(1.0, 1.0, 1.0, 0.5)
|
||||||
|
vec3 region_uv = get_index_uv(uv2);
|
||||||
|
vec4 color_map = region_uv.z > -1.0 ? textureLod(_color_maps, region_uv, region_mip) : COLOR_MAP;
|
||||||
|
|
||||||
|
Material mat[4];
|
||||||
|
uint control = floatBitsToUint(texelFetch(_control_maps, get_index_coord(floor(uv), FRAGMENT_PASS), 0)).r;
|
||||||
|
get_material(base_derivatives, control, region_uv, mat[3]);
|
||||||
|
|
||||||
|
vec4 albedo_height = mat[3].alb_ht;
|
||||||
|
vec4 normal_rough = mat[3].nrm_rg;
|
||||||
|
float normal_map_depth = mat[3].nrm_depth;
|
||||||
|
float ao_strength = mat[3].ao_str;
|
||||||
|
|
||||||
|
// Only do blend if we really have to.
|
||||||
|
if (v_lerp == 1) {
|
||||||
|
get_material(base_derivatives, v_control[0], region_uv, mat[0]);
|
||||||
|
get_material(base_derivatives, v_control[1], region_uv, mat[1]);
|
||||||
|
get_material(base_derivatives, v_control[2], region_uv, mat[2]);
|
||||||
|
|
||||||
|
// we dont need weights before this point when using vertex normals.
|
||||||
|
vec2 weight = fract(uv);
|
||||||
|
vec2 invert = 1.0 - weight;
|
||||||
|
vec4 weights = vec4(
|
||||||
|
invert.x * weight.y, // 0
|
||||||
|
weight.x * weight.y, // 1
|
||||||
|
weight.x * invert.y, // 2
|
||||||
|
invert.x * invert.y // 3
|
||||||
|
);
|
||||||
|
|
||||||
|
// Interpolate Albedo/Height/Normal/Roughness
|
||||||
|
albedo_height =
|
||||||
|
mat[0].alb_ht * weights[0] +
|
||||||
|
mat[1].alb_ht * weights[1] +
|
||||||
|
mat[2].alb_ht * weights[2] +
|
||||||
|
mat[3].alb_ht * weights[3] ;
|
||||||
|
|
||||||
|
normal_rough =
|
||||||
|
mat[0].nrm_rg * weights[0] +
|
||||||
|
mat[1].nrm_rg * weights[1] +
|
||||||
|
mat[2].nrm_rg * weights[2] +
|
||||||
|
mat[3].nrm_rg * weights[3] ;
|
||||||
|
|
||||||
|
normal_map_depth =
|
||||||
|
mat[0].nrm_depth * weights[0] +
|
||||||
|
mat[1].nrm_depth * weights[1] +
|
||||||
|
mat[2].nrm_depth * weights[2] +
|
||||||
|
mat[3].nrm_depth * weights[3] ;
|
||||||
|
|
||||||
|
ao_strength =
|
||||||
|
mat[0].ao_str * weights[0] +
|
||||||
|
mat[1].ao_str * weights[1] +
|
||||||
|
mat[2].ao_str * weights[2] +
|
||||||
|
mat[3].ao_str * weights[3] ;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wetness/roughness modifier, converting 0 - 1 range to -1 to 1 range
|
||||||
|
float roughness = fma(color_map.a - 0.5, 2.0, normal_rough.a);
|
||||||
|
|
||||||
|
// Apply PBR
|
||||||
|
ALBEDO = albedo_height.rgb * color_map.rgb;
|
||||||
|
ROUGHNESS = roughness;
|
||||||
|
SPECULAR = 1. - normal_rough.a;
|
||||||
|
NORMAL_MAP = pack_normal(normal_rough.rgb);
|
||||||
|
NORMAL_MAP_DEPTH = normal_map_depth;
|
||||||
|
|
||||||
|
// Higher and/or facing up, less occluded.
|
||||||
|
// This is also virtually free.
|
||||||
|
float ao = (1.0 - (albedo_height.a * log(2.1 - ao_strength))) * (1.0 - normal_rough.y);
|
||||||
|
AO = clamp(1.0 - ao * ao_strength, albedo_height.a, 1.0);
|
||||||
|
AO_LIGHT_AFFECT = albedo_height.a;
|
||||||
|
|
||||||
|
}
|
||||||
1
addons/terrain_3d/extras/lightweight.gdshader.uid
Normal file
1
addons/terrain_3d/extras/lightweight.gdshader.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://bbx2xhanpq5l3
|
||||||
163
addons/terrain_3d/extras/lowpoly_colormap.gdshader
Normal file
163
addons/terrain_3d/extras/lowpoly_colormap.gdshader
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
// Copyright © 2025 Cory Petkovsek, Roope Palmroos, and Contributors.
|
||||||
|
// This is an example of a minimal, low-poly style shader colored by the color map and wetness tools.
|
||||||
|
// No textures are needed or used in this shader.
|
||||||
|
|
||||||
|
shader_type spatial;
|
||||||
|
render_mode blend_mix,depth_draw_opaque,cull_back,diffuse_burley,specular_schlick_ggx,skip_vertex_transform;
|
||||||
|
|
||||||
|
// Defined Constants
|
||||||
|
#define SKIP_PASS 0
|
||||||
|
#define VERTEX_PASS 1
|
||||||
|
#define FRAGMENT_PASS 2
|
||||||
|
|
||||||
|
#if CURRENT_RENDERER == RENDERER_COMPATIBILITY
|
||||||
|
#define fma(a, b, c) ((a) * (b) + (c))
|
||||||
|
#define dFdxCoarse(a) dFdx(a)
|
||||||
|
#define dFdyCoarse(a) dFdy(a)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Private uniforms
|
||||||
|
uniform vec3 _camera_pos = vec3(0.f);
|
||||||
|
uniform float _mesh_size = 48.f;
|
||||||
|
uniform uint _background_mode = 1u; // NONE = 0, FLAT = 1, NOISE = 2
|
||||||
|
uniform uint _mouse_layer = 0x80000000u; // Layer 32
|
||||||
|
uniform float _vertex_spacing = 1.0;
|
||||||
|
uniform float _vertex_density = 1.0; // = 1/_vertex_spacing
|
||||||
|
uniform float _region_size = 1024.0;
|
||||||
|
uniform float _region_texel_size = 0.0009765625; // = 1/1024
|
||||||
|
uniform int _region_map_size = 32;
|
||||||
|
uniform int _region_map[1024];
|
||||||
|
uniform vec2 _region_locations[1024];
|
||||||
|
uniform highp sampler2DArray _height_maps : repeat_disable;
|
||||||
|
uniform highp sampler2DArray _control_maps : repeat_disable;
|
||||||
|
uniform highp sampler2DArray _color_maps : source_color, filter_nearest_mipmap, repeat_disable;
|
||||||
|
|
||||||
|
// Public uniforms
|
||||||
|
uniform vec3 default_albedo : source_color = vec3(.38, .35, .3);
|
||||||
|
uniform float default_roughness : hint_range(0.0, 1.0, 0.01) = 0.8;
|
||||||
|
|
||||||
|
// Varyings & Types
|
||||||
|
// Some are required for editor functions
|
||||||
|
varying float v_vertex_xz_dist;
|
||||||
|
varying vec3 v_vertex;
|
||||||
|
|
||||||
|
////////////////////////
|
||||||
|
// Vertex
|
||||||
|
////////////////////////
|
||||||
|
|
||||||
|
// Takes in world space XZ (UV) coordinates & search depth (only applicable for background mode none)
|
||||||
|
// Returns ivec3 with:
|
||||||
|
// XY: (0 to _region_size - 1) coordinates within a region
|
||||||
|
// Z: layer index used for texturearrays, -1 if not in a region
|
||||||
|
ivec3 get_index_coord(const vec2 uv, const int search) {
|
||||||
|
vec2 r_uv = round(uv);
|
||||||
|
vec2 o_uv = mod(r_uv,_region_size);
|
||||||
|
ivec2 pos;
|
||||||
|
int bounds, layer_index = -1;
|
||||||
|
for (int i = -1; i < clamp(search, SKIP_PASS, FRAGMENT_PASS); i++) {
|
||||||
|
if ((layer_index == -1 && _background_mode == 0u ) || i < 0) {
|
||||||
|
r_uv -= i == -1 ? vec2(0.0) : vec2(float(o_uv.x <= o_uv.y), float(o_uv.y <= o_uv.x));
|
||||||
|
pos = ivec2(floor((r_uv) * _region_texel_size)) + (_region_map_size / 2);
|
||||||
|
bounds = int(uint(pos.x | pos.y) < uint(_region_map_size));
|
||||||
|
layer_index = (_region_map[ pos.y * _region_map_size + pos.x ] * bounds - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ivec3(ivec2(mod(r_uv,_region_size)), layer_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Takes in descaled (world_space / region_size) world to region space XZ (UV2) coordinates, returns vec3 with:
|
||||||
|
// XY: (0. to 1.) coordinates within a region
|
||||||
|
// Z: layer index used for texturearrays, -1 if not in a region
|
||||||
|
vec3 get_index_uv(const vec2 uv2) {
|
||||||
|
ivec2 pos = ivec2(floor(uv2)) + (_region_map_size / 2);
|
||||||
|
int bounds = int(uint(pos.x | pos.y) < uint(_region_map_size));
|
||||||
|
int layer_index = _region_map[ pos.y * _region_map_size + pos.x ] * bounds - 1;
|
||||||
|
return vec3(uv2 - _region_locations[layer_index], float(layer_index));
|
||||||
|
}
|
||||||
|
|
||||||
|
void vertex() {
|
||||||
|
// Get vertex of flat plane in world coordinates and set world UV
|
||||||
|
v_vertex = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;
|
||||||
|
|
||||||
|
// Camera distance to vertex on flat plane
|
||||||
|
v_vertex_xz_dist = length(v_vertex.xz - _camera_pos.xz);
|
||||||
|
|
||||||
|
// Geomorph vertex, set end and start for linear height interpolate
|
||||||
|
float scale = MODEL_MATRIX[0][0];
|
||||||
|
float vertex_lerp = smoothstep(0.55, 0.95, (v_vertex_xz_dist / scale - _mesh_size - 4.0) / (_mesh_size - 2.0));
|
||||||
|
vec2 v_fract = fract(VERTEX.xz * 0.5) * 2.0;
|
||||||
|
// For LOD0 morph from a regular grid to an alternating grid to align with LOD1+
|
||||||
|
vec2 shift = (scale < _vertex_spacing + 1e-6) ? // LOD0 or not
|
||||||
|
// Shift from regular to symetric
|
||||||
|
mix(v_fract, vec2(v_fract.x, -v_fract.y),
|
||||||
|
round(fract(round(mod(v_vertex.z * _vertex_density, 4.0)) *
|
||||||
|
round(mod(v_vertex.x * _vertex_density, 4.0)) * 0.25))
|
||||||
|
) :
|
||||||
|
// Symetric shift
|
||||||
|
v_fract * round((fract(v_vertex.xz * 0.25 / scale) - 0.5) * 4.0);
|
||||||
|
vec2 start_pos = v_vertex.xz * _vertex_density;
|
||||||
|
vec2 end_pos = (v_vertex.xz - shift * scale) * _vertex_density;
|
||||||
|
v_vertex.xz -= shift * scale * vertex_lerp;
|
||||||
|
|
||||||
|
// UV coordinates in world space. Values are 0 to _region_size within regions
|
||||||
|
UV = v_vertex.xz * _vertex_density;
|
||||||
|
|
||||||
|
// UV coordinates in region space + texel offset. Values are 0 to 1 within regions
|
||||||
|
UV2 = fma(UV, vec2(_region_texel_size), vec2(0.5 * _region_texel_size));
|
||||||
|
|
||||||
|
// Discard vertices for Holes. 1 lookup
|
||||||
|
ivec3 region = get_index_coord(start_pos, VERTEX_PASS);
|
||||||
|
uint control = floatBitsToUint(texelFetch(_control_maps, region, 0)).r;
|
||||||
|
bool hole = bool(control >>2u & 0x1u);
|
||||||
|
|
||||||
|
// Show holes to all cameras except mouse camera (on exactly 1 layer)
|
||||||
|
if ( !(CAMERA_VISIBLE_LAYERS == _mouse_layer) &&
|
||||||
|
(hole || (_background_mode == 0u && region.z < 0))) {
|
||||||
|
v_vertex.x = 0. / 0.;
|
||||||
|
} else {
|
||||||
|
// Interpolate Geomorph Start & End, set height. 2 Lookups.
|
||||||
|
ivec3 uv_a = get_index_coord(start_pos, VERTEX_PASS);
|
||||||
|
ivec3 uv_b = get_index_coord(end_pos, VERTEX_PASS);
|
||||||
|
float h = mix(texelFetch(_height_maps, uv_a, 0).r, texelFetch(_height_maps, uv_b, 0).r, vertex_lerp);
|
||||||
|
v_vertex.y = h;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert model space to view space w/ skip_vertex_transform render mode
|
||||||
|
VERTEX = (VIEW_MATRIX * vec4(v_vertex, 1.0)).xyz;
|
||||||
|
NORMAL = normalize((MODELVIEW_MATRIX * vec4(NORMAL, 0.0)).xyz);
|
||||||
|
BINORMAL = normalize((MODELVIEW_MATRIX * vec4(BINORMAL, 0.0)).xyz);
|
||||||
|
TANGENT = normalize((MODELVIEW_MATRIX * vec4(TANGENT, 0.0)).xyz);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////
|
||||||
|
// Fragment
|
||||||
|
////////////////////////
|
||||||
|
|
||||||
|
void fragment() {
|
||||||
|
// Recover UVs
|
||||||
|
vec2 uv = UV;
|
||||||
|
vec2 uv2 = UV2;
|
||||||
|
|
||||||
|
// Apply terrain normals
|
||||||
|
vec3 ddx = dFdxCoarse(VERTEX);
|
||||||
|
vec3 ddy = dFdyCoarse(VERTEX);
|
||||||
|
NORMAL = normalize(cross(ddy, ddx));
|
||||||
|
TANGENT = normalize(cross(NORMAL, vec3(0.0, 0.0, 1.0)));
|
||||||
|
BINORMAL = normalize(cross(NORMAL, TANGENT));
|
||||||
|
|
||||||
|
// Determine if we're in a region or not (region_uv.z>0)
|
||||||
|
vec3 region_uv = get_index_uv(uv2);
|
||||||
|
|
||||||
|
// Colormap. 1 lookup
|
||||||
|
float lod = log2(max(length(ddx.xz), length(ddy.xz)) * _vertex_density);
|
||||||
|
vec4 color_map = region_uv.z > -1.0 ?
|
||||||
|
textureLod(_color_maps, region_uv, lod) : vec4(1., 1., 1., .5);
|
||||||
|
|
||||||
|
// Wetness/roughness modifier, converting 0 - 1 range to -1 to 1 range
|
||||||
|
float roughness = fma(color_map.a - 0.5, 2.0, default_roughness);
|
||||||
|
|
||||||
|
// Apply PBR
|
||||||
|
ALBEDO = default_albedo * color_map.rgb;
|
||||||
|
ROUGHNESS = roughness;
|
||||||
|
SPECULAR = 1.0 - roughness;
|
||||||
|
}
|
||||||
1
addons/terrain_3d/extras/lowpoly_colormap.gdshader.uid
Normal file
1
addons/terrain_3d/extras/lowpoly_colormap.gdshader.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://bda7fq1rh3nmv
|
||||||
134
addons/terrain_3d/extras/lowpoly_minimum.gdshader
Normal file
134
addons/terrain_3d/extras/lowpoly_minimum.gdshader
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
// Copyright © 2025 Cory Petkovsek, Roope Palmroos, and Contributors.
|
||||||
|
// This shader is a version of minimum.gdshader with flat normals for a low poly look.
|
||||||
|
// Increase vertex_spacing for a better result.
|
||||||
|
|
||||||
|
shader_type spatial;
|
||||||
|
render_mode blend_mix,depth_draw_opaque,cull_back,diffuse_burley,specular_schlick_ggx,skip_vertex_transform;
|
||||||
|
|
||||||
|
// Defined Constants
|
||||||
|
#define SKIP_PASS 0
|
||||||
|
#define VERTEX_PASS 1
|
||||||
|
#define FRAGMENT_PASS 2
|
||||||
|
|
||||||
|
#if CURRENT_RENDERER == RENDERER_COMPATIBILITY
|
||||||
|
#define fma(a, b, c) ((a) * (b) + (c))
|
||||||
|
#define dFdxCoarse(a) dFdx(a)
|
||||||
|
#define dFdyCoarse(a) dFdy(a)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Private uniforms
|
||||||
|
uniform vec3 _camera_pos = vec3(0.f);
|
||||||
|
uniform float _mesh_size = 48.f;
|
||||||
|
uniform uint _background_mode = 1u; // NONE = 0, FLAT = 1, NOISE = 2
|
||||||
|
uniform uint _mouse_layer = 0x80000000u; // Layer 32
|
||||||
|
uniform float _vertex_spacing = 1.0;
|
||||||
|
uniform float _vertex_density = 1.0; // = 1/_vertex_spacing
|
||||||
|
uniform float _region_size = 1024.0;
|
||||||
|
uniform float _region_texel_size = 0.0009765625; // = 1/1024
|
||||||
|
uniform int _region_map_size = 32;
|
||||||
|
uniform int _region_map[1024];
|
||||||
|
uniform vec2 _region_locations[1024];
|
||||||
|
uniform highp sampler2DArray _height_maps : repeat_disable;
|
||||||
|
uniform highp sampler2DArray _control_maps : repeat_disable;
|
||||||
|
|
||||||
|
// Varyings & Types
|
||||||
|
// Some are required for editor functions
|
||||||
|
varying float v_vertex_xz_dist;
|
||||||
|
varying vec3 v_vertex;
|
||||||
|
|
||||||
|
////////////////////////
|
||||||
|
// Vertex
|
||||||
|
////////////////////////
|
||||||
|
|
||||||
|
// Takes in world space XZ (UV) coordinates & search depth (only applicable for background mode none)
|
||||||
|
// Returns ivec3 with:
|
||||||
|
// XY: (0 to _region_size - 1) coordinates within a region
|
||||||
|
// Z: layer index used for texturearrays, -1 if not in a region
|
||||||
|
ivec3 get_index_coord(const vec2 uv, const int search) {
|
||||||
|
vec2 r_uv = round(uv);
|
||||||
|
vec2 o_uv = mod(r_uv,_region_size);
|
||||||
|
ivec2 pos;
|
||||||
|
int bounds, layer_index = -1;
|
||||||
|
for (int i = -1; i < clamp(search, SKIP_PASS, FRAGMENT_PASS); i++) {
|
||||||
|
if ((layer_index == -1 && _background_mode == 0u ) || i < 0) {
|
||||||
|
r_uv -= i == -1 ? vec2(0.0) : vec2(float(o_uv.x <= o_uv.y), float(o_uv.y <= o_uv.x));
|
||||||
|
pos = ivec2(floor((r_uv) * _region_texel_size)) + (_region_map_size / 2);
|
||||||
|
bounds = int(uint(pos.x | pos.y) < uint(_region_map_size));
|
||||||
|
layer_index = (_region_map[ pos.y * _region_map_size + pos.x ] * bounds - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ivec3(ivec2(mod(r_uv,_region_size)), layer_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void vertex() {
|
||||||
|
// Get vertex of flat plane in world coordinates and set world UV
|
||||||
|
v_vertex = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;
|
||||||
|
|
||||||
|
// Camera distance to vertex on flat plane
|
||||||
|
v_vertex_xz_dist = length(v_vertex.xz - _camera_pos.xz);
|
||||||
|
|
||||||
|
// Geomorph vertex, set end and start for linear height interpolate
|
||||||
|
float scale = MODEL_MATRIX[0][0];
|
||||||
|
float vertex_lerp = smoothstep(0.55, 0.95, (v_vertex_xz_dist / scale - _mesh_size - 4.0) / (_mesh_size - 2.0));
|
||||||
|
vec2 v_fract = fract(VERTEX.xz * 0.5) * 2.0;
|
||||||
|
// For LOD0 morph from a regular grid to an alternating grid to align with LOD1+
|
||||||
|
vec2 shift = (scale < _vertex_spacing + 1e-6) ? // LOD0 or not
|
||||||
|
// Shift from regular to symetric
|
||||||
|
mix(v_fract, vec2(v_fract.x, -v_fract.y),
|
||||||
|
round(fract(round(mod(v_vertex.z * _vertex_density, 4.0)) *
|
||||||
|
round(mod(v_vertex.x * _vertex_density, 4.0)) * 0.25))
|
||||||
|
) :
|
||||||
|
// Symetric shift
|
||||||
|
v_fract * round((fract(v_vertex.xz * 0.25 / scale) - 0.5) * 4.0);
|
||||||
|
vec2 start_pos = v_vertex.xz * _vertex_density;
|
||||||
|
vec2 end_pos = (v_vertex.xz - shift * scale) * _vertex_density;
|
||||||
|
v_vertex.xz -= shift * scale * vertex_lerp;
|
||||||
|
|
||||||
|
// UV coordinates in world space. Values are 0 to _region_size within regions
|
||||||
|
UV = v_vertex.xz * _vertex_density;
|
||||||
|
|
||||||
|
// UV coordinates in region space + texel offset. Values are 0 to 1 within regions
|
||||||
|
UV2 = fma(UV, vec2(_region_texel_size), vec2(0.5 * _region_texel_size));
|
||||||
|
|
||||||
|
// Discard vertices for Holes. 1 lookup
|
||||||
|
ivec3 region = get_index_coord(start_pos, VERTEX_PASS);
|
||||||
|
uint control = floatBitsToUint(texelFetch(_control_maps, region, 0)).r;
|
||||||
|
bool hole = bool(control >>2u & 0x1u);
|
||||||
|
|
||||||
|
// Show holes to all cameras except mouse camera (on exactly 1 layer)
|
||||||
|
if ( !(CAMERA_VISIBLE_LAYERS == _mouse_layer) &&
|
||||||
|
(hole || (_background_mode == 0u && region.z < 0))) {
|
||||||
|
v_vertex.x = 0. / 0.;
|
||||||
|
} else {
|
||||||
|
// Interpolate Geomorph Start & End, set height. 2 Lookups.
|
||||||
|
ivec3 uv_a = get_index_coord(start_pos, VERTEX_PASS);
|
||||||
|
ivec3 uv_b = get_index_coord(end_pos, VERTEX_PASS);
|
||||||
|
float h = mix(texelFetch(_height_maps, uv_a, 0).r, texelFetch(_height_maps, uv_b, 0).r, vertex_lerp);
|
||||||
|
v_vertex.y = h;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert model space to view space w/ skip_vertex_transform render mode
|
||||||
|
VERTEX = (VIEW_MATRIX * vec4(v_vertex, 1.0)).xyz;
|
||||||
|
NORMAL = normalize((MODELVIEW_MATRIX * vec4(NORMAL, 0.0)).xyz);
|
||||||
|
BINORMAL = normalize((MODELVIEW_MATRIX * vec4(BINORMAL, 0.0)).xyz);
|
||||||
|
TANGENT = normalize((MODELVIEW_MATRIX * vec4(TANGENT, 0.0)).xyz);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////
|
||||||
|
// Fragment
|
||||||
|
////////////////////////
|
||||||
|
|
||||||
|
void fragment() {
|
||||||
|
// Recover UVs
|
||||||
|
vec2 uv = UV;
|
||||||
|
vec2 uv2 = UV2;
|
||||||
|
|
||||||
|
// Apply terrain normals
|
||||||
|
NORMAL = normalize(cross(dFdyCoarse(VERTEX),dFdxCoarse(VERTEX)));
|
||||||
|
TANGENT = normalize(cross(NORMAL, vec3(0.0, 0.0, 1.0)));
|
||||||
|
BINORMAL = normalize(cross(NORMAL, TANGENT));
|
||||||
|
|
||||||
|
// Apply PBR
|
||||||
|
ALBEDO = vec3(.2);
|
||||||
|
ROUGHNESS = .7;
|
||||||
|
}
|
||||||
1
addons/terrain_3d/extras/lowpoly_minimum.gdshader.uid
Normal file
1
addons/terrain_3d/extras/lowpoly_minimum.gdshader.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://x11v7w7v8hqa
|
||||||
@@ -1,125 +1,120 @@
|
|||||||
|
// Copyright © 2025 Cory Petkovsek, Roope Palmroos, and Contributors.
|
||||||
// This shader is the minimum needed to allow the terrain to function, without any texturing.
|
// This shader is the minimum needed to allow the terrain to function, without any texturing.
|
||||||
|
|
||||||
shader_type spatial;
|
shader_type spatial;
|
||||||
render_mode blend_mix,depth_draw_opaque,cull_back,diffuse_burley,specular_schlick_ggx,skip_vertex_transform;
|
render_mode blend_mix,depth_draw_opaque,cull_back,diffuse_burley,specular_schlick_ggx,skip_vertex_transform;
|
||||||
|
|
||||||
|
// Defined Constants
|
||||||
|
#define SKIP_PASS 0
|
||||||
|
#define VERTEX_PASS 1
|
||||||
|
#define FRAGMENT_PASS 2
|
||||||
|
|
||||||
|
#if CURRENT_RENDERER == RENDERER_COMPATIBILITY
|
||||||
|
#define fma(a, b, c) ((a) * (b) + (c))
|
||||||
|
#define dFdxCoarse(a) dFdx(a)
|
||||||
|
#define dFdyCoarse(a) dFdy(a)
|
||||||
|
#endif
|
||||||
|
|
||||||
// Private uniforms
|
// Private uniforms
|
||||||
uniform float _region_size = 1024.0;
|
// Commented uniforms aren't needed for this shader, but are available for your own needs.
|
||||||
uniform float _region_texel_size = 0.0009765625; // = 1/1024
|
uniform vec3 _camera_pos = vec3(0.f);
|
||||||
|
uniform float _mesh_size = 48.f;
|
||||||
|
uniform uint _background_mode = 1u; // NONE = 0, FLAT = 1, NOISE = 2
|
||||||
|
uniform uint _mouse_layer = 0x80000000u; // Layer 32
|
||||||
uniform float _vertex_spacing = 1.0;
|
uniform float _vertex_spacing = 1.0;
|
||||||
uniform float _vertex_density = 1.0; // = 1/_vertex_spacing
|
uniform float _vertex_density = 1.0; // = 1/_vertex_spacing
|
||||||
|
uniform float _region_size = 1024.0;
|
||||||
|
uniform float _region_texel_size = 0.0009765625; // = 1/1024
|
||||||
uniform int _region_map_size = 32;
|
uniform int _region_map_size = 32;
|
||||||
uniform int _region_map[1024];
|
uniform int _region_map[1024];
|
||||||
uniform vec2 _region_locations[1024];
|
uniform vec2 _region_locations[1024];
|
||||||
uniform sampler2DArray _height_maps : repeat_disable;
|
//uniform float _texture_uv_scale_array[32];
|
||||||
uniform usampler2DArray _control_maps : repeat_disable;
|
//uniform float _texture_detile_array[32];
|
||||||
uniform sampler2DArray _color_maps : source_color, filter_linear_mipmap_anisotropic, repeat_disable;
|
//uniform vec4 _texture_color_array[32];
|
||||||
uniform sampler2DArray _texture_array_albedo : source_color, filter_linear_mipmap_anisotropic, repeat_enable;
|
uniform highp sampler2DArray _height_maps : repeat_disable;
|
||||||
uniform sampler2DArray _texture_array_normal : hint_normal, filter_linear_mipmap_anisotropic, repeat_enable;
|
uniform highp sampler2DArray _control_maps : repeat_disable;
|
||||||
uniform sampler2D noise_texture : source_color, filter_linear_mipmap_anisotropic, repeat_enable;
|
//uniform highp sampler2DArray _color_maps : source_color, filter_linear_mipmap_anisotropic, repeat_disable;
|
||||||
|
//uniform highp sampler2DArray _texture_array_albedo : source_color, filter_linear_mipmap_anisotropic, repeat_enable;
|
||||||
uniform float _texture_uv_scale_array[32];
|
//uniform highp sampler2DArray _texture_array_normal : hint_normal, filter_linear_mipmap_anisotropic, repeat_enable;
|
||||||
uniform float _texture_detile_array[32];
|
|
||||||
uniform vec4 _texture_color_array[32];
|
|
||||||
uniform uint _background_mode = 1u; // NONE = 0, FLAT = 1, NOISE = 2
|
|
||||||
uniform uint _mouse_layer = 0x80000000u; // Layer 32
|
|
||||||
|
|
||||||
// Public uniforms
|
|
||||||
uniform float vertex_normals_distance : hint_range(0, 1024) = 128.0;
|
|
||||||
|
|
||||||
// Varyings & Types
|
// Varyings & Types
|
||||||
varying flat vec3 v_vertex; // World coordinate vertex location
|
// Some are required for editor functions
|
||||||
varying flat vec3 v_camera_pos;
|
|
||||||
varying float v_vertex_xz_dist;
|
varying float v_vertex_xz_dist;
|
||||||
varying flat ivec3 v_region;
|
varying vec3 v_vertex;
|
||||||
varying flat vec2 v_uv_offset;
|
|
||||||
varying flat vec2 v_uv2_offset;
|
|
||||||
varying vec3 v_normal;
|
|
||||||
varying float v_region_border_mask;
|
|
||||||
|
|
||||||
////////////////////////
|
////////////////////////
|
||||||
// Vertex
|
// Vertex
|
||||||
////////////////////////
|
////////////////////////
|
||||||
|
|
||||||
// Takes in UV world space coordinates, returns ivec3 with:
|
// Takes in world space XZ (UV) coordinates & search depth (only applicable for background mode none)
|
||||||
// XY: (0 to _region_size) coordinates within a region
|
// Returns ivec3 with:
|
||||||
|
// XY: (0 to _region_size - 1) coordinates within a region
|
||||||
// Z: layer index used for texturearrays, -1 if not in a region
|
// Z: layer index used for texturearrays, -1 if not in a region
|
||||||
ivec3 get_region_uv(const vec2 uv) {
|
ivec3 get_index_coord(const vec2 uv, const int search) {
|
||||||
ivec2 pos = ivec2(floor(uv * _region_texel_size)) + (_region_map_size / 2);
|
vec2 r_uv = round(uv);
|
||||||
int bounds = int(uint(pos.x | pos.y) < uint(_region_map_size));
|
vec2 o_uv = mod(r_uv,_region_size);
|
||||||
int layer_index = _region_map[ pos.y * _region_map_size + pos.x ] * bounds - 1;
|
ivec2 pos;
|
||||||
return ivec3(ivec2(mod(uv,_region_size)), layer_index);
|
int bounds, layer_index = -1;
|
||||||
}
|
for (int i = -1; i < clamp(search, SKIP_PASS, FRAGMENT_PASS); i++) {
|
||||||
|
if ((layer_index == -1 && _background_mode == 0u ) || i < 0) {
|
||||||
// Takes in UV2 region space coordinates, returns vec3 with:
|
r_uv -= i == -1 ? vec2(0.0) : vec2(float(o_uv.x <= o_uv.y), float(o_uv.y <= o_uv.x));
|
||||||
// XY: (0 to 1) coordinates within a region
|
pos = ivec2(floor((r_uv) * _region_texel_size)) + (_region_map_size / 2);
|
||||||
// Z: layer index used for texturearrays, -1 if not in a region
|
bounds = int(uint(pos.x | pos.y) < uint(_region_map_size));
|
||||||
vec3 get_region_uv2(const vec2 uv2) {
|
layer_index = (_region_map[ pos.y * _region_map_size + pos.x ] * bounds - 1);
|
||||||
// Remove Texel Offset to ensure correct region index.
|
|
||||||
ivec2 pos = ivec2(floor(uv2 - vec2(_region_texel_size * 0.5))) + (_region_map_size / 2);
|
|
||||||
int bounds = int(uint(pos.x | pos.y) < uint(_region_map_size));
|
|
||||||
int layer_index = _region_map[ pos.y * _region_map_size + pos.x ] * bounds - 1;
|
|
||||||
return vec3(uv2 - _region_locations[layer_index], float(layer_index));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1 lookup
|
|
||||||
float get_height(vec2 uv) {
|
|
||||||
highp float height = 0.0;
|
|
||||||
vec3 region = get_region_uv2(uv);
|
|
||||||
if (region.z >= 0.) {
|
|
||||||
height = texture(_height_maps, region).r;
|
|
||||||
}
|
}
|
||||||
return height;
|
}
|
||||||
|
return ivec3(ivec2(mod(r_uv,_region_size)), layer_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
void vertex() {
|
void vertex() {
|
||||||
// Get camera pos in world vertex coords
|
|
||||||
v_camera_pos = INV_VIEW_MATRIX[3].xyz;
|
|
||||||
|
|
||||||
// Get vertex of flat plane in world coordinates and set world UV
|
// Get vertex of flat plane in world coordinates and set world UV
|
||||||
v_vertex = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;
|
v_vertex = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;
|
||||||
|
|
||||||
// Camera distance to vertex on flat plane
|
// Camera distance to vertex on flat plane
|
||||||
v_vertex_xz_dist = length(v_vertex.xz - v_camera_pos.xz);
|
v_vertex_xz_dist = length(v_vertex.xz - _camera_pos.xz);
|
||||||
|
|
||||||
|
// Geomorph vertex, set end and start for linear height interpolate
|
||||||
|
float scale = MODEL_MATRIX[0][0];
|
||||||
|
float vertex_lerp = smoothstep(0.55, 0.95, (v_vertex_xz_dist / scale - _mesh_size - 4.0) / (_mesh_size - 2.0));
|
||||||
|
vec2 v_fract = fract(VERTEX.xz * 0.5) * 2.0;
|
||||||
|
// For LOD0 morph from a regular grid to an alternating grid to align with LOD1+
|
||||||
|
vec2 shift = (scale < _vertex_spacing + 1e-6) ? // LOD0 or not
|
||||||
|
// Shift from regular to symetric
|
||||||
|
mix(v_fract, vec2(v_fract.x, -v_fract.y),
|
||||||
|
round(fract(round(mod(v_vertex.z * _vertex_density, 4.0)) *
|
||||||
|
round(mod(v_vertex.x * _vertex_density, 4.0)) * 0.25))
|
||||||
|
) :
|
||||||
|
// Symetric shift
|
||||||
|
v_fract * round((fract(v_vertex.xz * 0.25 / scale) - 0.5) * 4.0);
|
||||||
|
vec2 start_pos = v_vertex.xz * _vertex_density;
|
||||||
|
vec2 end_pos = (v_vertex.xz - shift * scale) * _vertex_density;
|
||||||
|
v_vertex.xz -= shift * scale * vertex_lerp;
|
||||||
|
|
||||||
// UV coordinates in world space. Values are 0 to _region_size within regions
|
// UV coordinates in world space. Values are 0 to _region_size within regions
|
||||||
UV = round(v_vertex.xz * _vertex_density);
|
UV = v_vertex.xz * _vertex_density;
|
||||||
|
|
||||||
// UV coordinates in region space + texel offset. Values are 0 to 1 within regions
|
// UV coordinates in region space + texel offset. Values are 0 to 1 within regions
|
||||||
UV2 = fma(UV, vec2(_region_texel_size), vec2(0.5 * _region_texel_size));
|
UV2 = fma(UV, vec2(_region_texel_size), vec2(0.5 * _region_texel_size));
|
||||||
|
|
||||||
// Discard vertices for Holes. 1 lookup
|
// Discard vertices for Holes. 1 lookup
|
||||||
v_region = get_region_uv(UV);
|
ivec3 v_region = get_index_coord(start_pos, VERTEX_PASS);
|
||||||
uint control = texelFetch(_control_maps, v_region, 0).r;
|
uint control = floatBitsToUint(texelFetch(_control_maps, v_region, 0)).r;
|
||||||
bool hole = bool(control >>2u & 0x1u);
|
bool hole = bool(control >>2u & 0x1u);
|
||||||
|
|
||||||
// Show holes to all cameras except mouse camera (on exactly 1 layer)
|
// Show holes to all cameras except mouse camera (on exactly 1 layer)
|
||||||
if ( !(CAMERA_VISIBLE_LAYERS == _mouse_layer) &&
|
if ( !(CAMERA_VISIBLE_LAYERS == _mouse_layer) &&
|
||||||
(hole || (_background_mode == 0u && (get_region_uv(UV - _region_texel_size) & v_region).z < 0))) {
|
(hole || (_background_mode == 0u && v_region.z < 0))) {
|
||||||
VERTEX.x = 0. / 0.;
|
v_vertex.x = 0. / 0.;
|
||||||
} else {
|
} else {
|
||||||
// Set final vertex height & calculate vertex normals. 3 lookups.
|
// Interpolate Geomorph Start & End, set height. 2 Lookups.
|
||||||
VERTEX.y = get_height(UV2);
|
ivec3 uv_a = get_index_coord(start_pos, VERTEX_PASS);
|
||||||
v_vertex.y = VERTEX.y;
|
ivec3 uv_b = get_index_coord(end_pos, VERTEX_PASS);
|
||||||
v_normal = vec3(
|
float h = mix(texelFetch(_height_maps, uv_a, 0).r, texelFetch(_height_maps, uv_b, 0).r, vertex_lerp);
|
||||||
v_vertex.y - get_height(UV2 + vec2(_region_texel_size, 0)),
|
v_vertex.y = h;
|
||||||
_vertex_spacing,
|
|
||||||
v_vertex.y - get_height(UV2 + vec2(0, _region_texel_size))
|
|
||||||
);
|
|
||||||
// Due to a bug caused by the GPUs linear interpolation across edges of region maps,
|
|
||||||
// mask region edges and use vertex normals only across region boundaries.
|
|
||||||
v_region_border_mask = mod(UV.x + 2.5, _region_size) - fract(UV.x) < 5.0 || mod(UV.y + 2.5, _region_size) - fract(UV.y) < 5.0 ? 1. : 0.;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transform UVs to local to avoid poor precision during varying interpolation.
|
|
||||||
v_uv_offset = MODEL_MATRIX[3].xz * _vertex_density;
|
|
||||||
UV -= v_uv_offset;
|
|
||||||
v_uv2_offset = v_uv_offset * _region_texel_size;
|
|
||||||
UV2 -= v_uv2_offset;
|
|
||||||
|
|
||||||
// Convert model space to view space w/ skip_vertex_transform render mode
|
// Convert model space to view space w/ skip_vertex_transform render mode
|
||||||
VERTEX = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;
|
VERTEX = (VIEW_MATRIX * vec4(v_vertex, 1.0)).xyz;
|
||||||
VERTEX = (VIEW_MATRIX * vec4(VERTEX, 1.0)).xyz;
|
|
||||||
NORMAL = normalize((MODELVIEW_MATRIX * vec4(NORMAL, 0.0)).xyz);
|
NORMAL = normalize((MODELVIEW_MATRIX * vec4(NORMAL, 0.0)).xyz);
|
||||||
BINORMAL = normalize((MODELVIEW_MATRIX * vec4(BINORMAL, 0.0)).xyz);
|
BINORMAL = normalize((MODELVIEW_MATRIX * vec4(BINORMAL, 0.0)).xyz);
|
||||||
TANGENT = normalize((MODELVIEW_MATRIX * vec4(TANGENT, 0.0)).xyz);
|
TANGENT = normalize((MODELVIEW_MATRIX * vec4(TANGENT, 0.0)).xyz);
|
||||||
@@ -129,36 +124,85 @@ void vertex() {
|
|||||||
// Fragment
|
// Fragment
|
||||||
////////////////////////
|
////////////////////////
|
||||||
|
|
||||||
// 0 - 3 lookups
|
|
||||||
vec3 get_normal(vec2 uv, out vec3 tangent, out vec3 binormal) {
|
|
||||||
float u, v, height;
|
|
||||||
vec3 normal;
|
|
||||||
// Use vertex normals within radius of vertex_normals_distance, and along region borders.
|
|
||||||
if (v_region_border_mask > 0.5 || v_vertex_xz_dist < vertex_normals_distance) {
|
|
||||||
normal = normalize(v_normal);
|
|
||||||
} else {
|
|
||||||
height = get_height(uv);
|
|
||||||
u = height - get_height(uv + vec2(_region_texel_size, 0));
|
|
||||||
v = height - get_height(uv + vec2(0, _region_texel_size));
|
|
||||||
normal = normalize(vec3(u, _vertex_spacing, v));
|
|
||||||
}
|
|
||||||
tangent = cross(normal, vec3(0, 0, 1));
|
|
||||||
binormal = cross(normal, tangent);
|
|
||||||
return normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
void fragment() {
|
void fragment() {
|
||||||
// Recover UVs
|
// Recover UVs
|
||||||
vec2 uv = UV + v_uv_offset;
|
vec2 uv = UV;
|
||||||
vec2 uv2 = UV2 + v_uv2_offset;
|
vec2 uv2 = UV2;
|
||||||
|
|
||||||
// Calculate Terrain Normals. 4 lookups
|
// Lookup offsets, ID and blend weight
|
||||||
vec3 w_tangent, w_binormal;
|
const vec3 offsets = vec3(0, 1, 2);
|
||||||
vec3 w_normal = get_normal(uv2, w_tangent, w_binormal);
|
vec2 index_id = floor(uv);
|
||||||
|
vec2 weight = fract(uv);
|
||||||
|
vec2 invert = 1.0 - weight;
|
||||||
|
vec4 weights = vec4(
|
||||||
|
invert.x * weight.y, // 0
|
||||||
|
weight.x * weight.y, // 1
|
||||||
|
weight.x * invert.y, // 2
|
||||||
|
invert.x * invert.y // 3
|
||||||
|
);
|
||||||
|
|
||||||
|
vec3 base_ddx = dFdxCoarse(v_vertex);
|
||||||
|
vec3 base_ddy = dFdyCoarse(v_vertex);
|
||||||
|
vec4 base_derivatives = vec4(base_ddx.xz, base_ddy.xz);
|
||||||
|
// Calculate the effective mipmap for regionspace, and if less than 0,
|
||||||
|
// skip all extra lookups required for bilinear blend.
|
||||||
|
float region_mip = log2(max(length(base_ddx.xz), length(base_ddy.xz)) * _vertex_density);
|
||||||
|
bool bilerp = region_mip < 0.0;
|
||||||
|
|
||||||
|
ivec3 indexUV[4];
|
||||||
|
// control map lookups, used for some normal lookups as well
|
||||||
|
indexUV[0] = get_index_coord(index_id + offsets.xy, FRAGMENT_PASS);
|
||||||
|
indexUV[1] = get_index_coord(index_id + offsets.yy, FRAGMENT_PASS);
|
||||||
|
indexUV[2] = get_index_coord(index_id + offsets.yx, FRAGMENT_PASS);
|
||||||
|
indexUV[3] = get_index_coord(index_id + offsets.xx, FRAGMENT_PASS);
|
||||||
|
|
||||||
|
// Terrain normals
|
||||||
|
vec3 index_normal[4];
|
||||||
|
float h[8];
|
||||||
|
// allows additional derivatives, eg world noise, brush previews etc
|
||||||
|
float u = 0.0;
|
||||||
|
float v = 0.0;
|
||||||
|
|
||||||
|
// Re-use the indexUVs for the first lookups, skipping some math. 3 lookups
|
||||||
|
h[3] = texelFetch(_height_maps, indexUV[3], 0).r; // 0 (0,0)
|
||||||
|
h[2] = texelFetch(_height_maps, indexUV[2], 0).r; // 1 (1,0)
|
||||||
|
h[0] = texelFetch(_height_maps, indexUV[0], 0).r; // 2 (0,1)
|
||||||
|
index_normal[3] = normalize(vec3(h[3] - h[2] + u, _vertex_spacing, h[3] - h[0] + v));
|
||||||
|
|
||||||
|
// Set flat world normal - overriden if bilerp is true
|
||||||
|
vec3 w_normal = index_normal[3];
|
||||||
|
|
||||||
|
// Branching smooth normals must be done seperatley for correct normals at all 4 index ids
|
||||||
|
if (bilerp) {
|
||||||
|
// 5 lookups
|
||||||
|
// Fetch the additional required height values for smooth normals
|
||||||
|
h[1] = texelFetch(_height_maps, indexUV[1], 0).r; // 3 (1,1)
|
||||||
|
h[4] = texelFetch(_height_maps, get_index_coord(index_id + offsets.yz, FRAGMENT_PASS), 0).r; // 4 (1,2)
|
||||||
|
h[5] = texelFetch(_height_maps, get_index_coord(index_id + offsets.zy, FRAGMENT_PASS), 0).r; // 5 (2,1)
|
||||||
|
h[6] = texelFetch(_height_maps, get_index_coord(index_id + offsets.zx, FRAGMENT_PASS), 0).r; // 6 (2,0)
|
||||||
|
h[7] = texelFetch(_height_maps, get_index_coord(index_id + offsets.xz, FRAGMENT_PASS), 0).r; // 7 (0,2)
|
||||||
|
|
||||||
|
// Calculate the normal for the remaining index ids.
|
||||||
|
index_normal[0] = normalize(vec3(h[0] - h[1] + u, _vertex_spacing, h[0] - h[7] + v));
|
||||||
|
index_normal[1] = normalize(vec3(h[1] - h[5] + u, _vertex_spacing, h[1] - h[4] + v));
|
||||||
|
index_normal[2] = normalize(vec3(h[2] - h[6] + u, _vertex_spacing, h[2] - h[1] + v));
|
||||||
|
|
||||||
|
// Set interpolated world normal
|
||||||
|
w_normal =
|
||||||
|
index_normal[0] * weights[0] +
|
||||||
|
index_normal[1] * weights[1] +
|
||||||
|
index_normal[2] * weights[2] +
|
||||||
|
index_normal[3] * weights[3] ;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply terrain normals
|
||||||
|
vec3 w_tangent = normalize(cross(w_normal, vec3(0.0, 0.0, 1.0)));
|
||||||
|
vec3 w_binormal = normalize(cross(w_normal, w_tangent));
|
||||||
NORMAL = mat3(VIEW_MATRIX) * w_normal;
|
NORMAL = mat3(VIEW_MATRIX) * w_normal;
|
||||||
TANGENT = mat3(VIEW_MATRIX) * w_tangent;
|
TANGENT = mat3(VIEW_MATRIX) * w_tangent;
|
||||||
BINORMAL = mat3(VIEW_MATRIX) * w_binormal;
|
BINORMAL = mat3(VIEW_MATRIX) * w_binormal;
|
||||||
|
|
||||||
// Apply PBR
|
// Apply PBR
|
||||||
ALBEDO=vec3(.2);
|
ALBEDO = vec3(.2);
|
||||||
|
ROUGHNESS = .7;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
uid://c8qog5mlaoeno
|
uid://01qauauvd8aa
|
||||||
|
|||||||
@@ -1,15 +1,11 @@
|
|||||||
|
# Copyright © 2025 Cory Petkovsek, Roope Palmroos, and Contributors.
|
||||||
# This script is an addon for HungryProton's Scatter https://github.com/HungryProton/scatter
|
# This script is an addon for HungryProton's Scatter https://github.com/HungryProton/scatter
|
||||||
# It provides a `Project on Terrain3D` modifier, which allows Scatter
|
# It provides a `Project on Terrain3D` modifier, which allows Scatter
|
||||||
# to detect the terrain height from Terrain3D without using collision.
|
# to detect the terrain height from Terrain3D without using collision.
|
||||||
# Copy this file into /addons/proton_scatter/src/modifiers
|
#
|
||||||
# Then uncomment everything below
|
|
||||||
# In the editor, add this modifier to Scatter, then set your Terrain3D node
|
|
||||||
|
|
||||||
# This script is an addon for HungryProton's Scatter https://github.com/HungryProton/scatter
|
|
||||||
# It allows Scatter to detect the terrain height from Terrain3D
|
|
||||||
# Copy this file into /addons/proton_scatter/src/modifiers
|
# Copy this file into /addons/proton_scatter/src/modifiers
|
||||||
# Then uncomment everything below (select, press CTRL+K)
|
# Then uncomment everything below (select, press CTRL+K)
|
||||||
# In the editor, add this modifier, then set your Terrain3D node
|
# In the editor, add this modifier to Scatter, then set your Terrain3D node
|
||||||
|
|
||||||
#@tool
|
#@tool
|
||||||
#extends "base_modifier.gd"
|
#extends "base_modifier.gd"
|
||||||
@@ -20,6 +16,7 @@
|
|||||||
#
|
#
|
||||||
#@export var terrain_node : NodePath
|
#@export var terrain_node : NodePath
|
||||||
#@export var align_with_collision_normal := false
|
#@export var align_with_collision_normal := false
|
||||||
|
#@export_range(0.0, 90.0, 0.1) var max_slope = 90.0
|
||||||
#
|
#
|
||||||
#var _terrain: Terrain3D
|
#var _terrain: Terrain3D
|
||||||
#
|
#
|
||||||
@@ -68,20 +65,29 @@
|
|||||||
## Get global transform
|
## Get global transform
|
||||||
#var gt: Transform3D = domain.get_global_transform()
|
#var gt: Transform3D = domain.get_global_transform()
|
||||||
#var gt_inverse := gt.affine_inverse()
|
#var gt_inverse := gt.affine_inverse()
|
||||||
|
#var new_transforms_array: Array[Transform3D] = []
|
||||||
|
#var remapped_max_slope: float = remap(max_slope, 0.0, 90.0, 0.0, 1.0)
|
||||||
#for i in transforms.list.size():
|
#for i in transforms.list.size():
|
||||||
#var location: Vector3 = (gt * transforms.list[i]).origin
|
#var t: Transform3D = transforms.list[i]
|
||||||
|
#
|
||||||
|
#var location: Vector3 = (gt * t).origin
|
||||||
#var height: float = _terrain.data.get_height(location)
|
#var height: float = _terrain.data.get_height(location)
|
||||||
#var normal: Vector3 = _terrain.data.get_normal(location)
|
#var normal: Vector3 = _terrain.data.get_normal(location)
|
||||||
#
|
#
|
||||||
#if align_with_collision_normal and not is_nan(normal.x):
|
#if align_with_collision_normal and not is_nan(normal.x):
|
||||||
#transforms.list[i].basis.y = normal
|
#t.basis.y = normal
|
||||||
#transforms.list[i].basis.x = -transforms.list[i].basis.z.cross(normal)
|
#t.basis.x = -t.basis.z.cross(normal)
|
||||||
#transforms.list[i].basis = transforms.list[i].basis.orthonormalized()
|
#t.basis = t.basis.orthonormalized()
|
||||||
#
|
#
|
||||||
#transforms.list[i].origin.y = gt.origin.y if is_nan(height) else height - gt.origin.y
|
#if abs(Vector3.UP.dot(normal)) >= (1.0 - remapped_max_slope):
|
||||||
|
#t.origin.y = gt.origin.y if is_nan(height) else height - gt.origin.y
|
||||||
|
#new_transforms_array.push_back(t)
|
||||||
|
#
|
||||||
|
#transforms.list.clear()
|
||||||
|
#transforms.list.append_array(new_transforms_array)
|
||||||
#
|
#
|
||||||
#if transforms.is_empty():
|
#if transforms.is_empty():
|
||||||
#warning += """Every point has been removed. Possible reasons include: \n
|
#warning += """All transforms have been removed. Possible reasons include: \n
|
||||||
#+ No collider is close enough to the shapes.
|
#+ No collider is close enough to the shapes.
|
||||||
#+ Ray length is too short.
|
#+ Ray length is too short.
|
||||||
#+ Ray direction is incorrect.
|
#+ Ray direction is incorrect.
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
uid://ce403ehalp57b
|
uid://g3opjh3m3iww
|
||||||
|
|||||||
52
addons/terrain_3d/extras/region_mover.gd
Normal file
52
addons/terrain_3d/extras/region_mover.gd
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
# This script can be used to move your regions by an offset.
|
||||||
|
# Eventually this tool will find its way into a built in UI
|
||||||
|
#
|
||||||
|
# Attach it to your Terrain3D node
|
||||||
|
# Save and reload your scene
|
||||||
|
# Select your Terrain3D node
|
||||||
|
# Enter a valid `offset` where all regions will be within -16, +15
|
||||||
|
# Run it
|
||||||
|
# It should unload the regions, rename files, and reload them
|
||||||
|
# Clear the script and resave your scene
|
||||||
|
|
||||||
|
|
||||||
|
@tool
|
||||||
|
extends Terrain3D
|
||||||
|
|
||||||
|
|
||||||
|
@export var offset: Vector2i
|
||||||
|
@export var run: bool = false : set = start_rename
|
||||||
|
|
||||||
|
|
||||||
|
func start_rename(val: bool = false) -> void:
|
||||||
|
if val == false or offset == Vector2i.ZERO:
|
||||||
|
return
|
||||||
|
|
||||||
|
var dir_name: String = data_directory
|
||||||
|
data_directory = ""
|
||||||
|
var dir := DirAccess.open(dir_name)
|
||||||
|
if not dir:
|
||||||
|
print("An error occurred when trying to access the path: ", data_directory)
|
||||||
|
return
|
||||||
|
|
||||||
|
var affected_files: PackedStringArray
|
||||||
|
var files: PackedStringArray = dir.get_files()
|
||||||
|
for file_name in files:
|
||||||
|
if file_name.match("terrain3d*.res") and not dir.current_is_dir():
|
||||||
|
var region_loc: Vector2i = Terrain3DUtil.filename_to_location(file_name)
|
||||||
|
var new_loc: Vector2i = region_loc + offset
|
||||||
|
if new_loc.x < -16 or new_loc.x > 15 or new_loc.y < -16 or new_loc.y > 15:
|
||||||
|
push_error("New location %.0v out of bounds for region %.0v. Aborting" % [ new_loc, region_loc ])
|
||||||
|
return
|
||||||
|
var new_name: String = "tmp_" + Terrain3DUtil.location_to_filename(new_loc)
|
||||||
|
dir.rename(file_name, new_name)
|
||||||
|
affected_files.push_back(new_name)
|
||||||
|
print("File: %s renamed to: %s" % [ file_name, new_name ])
|
||||||
|
|
||||||
|
for file_name in affected_files:
|
||||||
|
var new_name: String = file_name.trim_prefix("tmp_")
|
||||||
|
dir.rename(file_name, new_name)
|
||||||
|
print("File: %s renamed to: %s" % [ file_name, new_name ])
|
||||||
|
|
||||||
|
data_directory = dir_name
|
||||||
|
EditorInterface.get_resource_filesystem().scan()
|
||||||
1
addons/terrain_3d/extras/region_mover.gd.uid
Normal file
1
addons/terrain_3d/extras/region_mover.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://bngnvtbm6ifkk
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
# Copyright © 2025 Cory Petkovsek, Roope Palmroos, and Contributors.
|
||||||
|
# Bake LOD Dialog for Terrain3D
|
||||||
@tool
|
@tool
|
||||||
extends ConfirmationDialog
|
extends ConfirmationDialog
|
||||||
|
|
||||||
1
addons/terrain_3d/menu/bake_lod_dialog.gd.uid
Normal file
1
addons/terrain_3d/menu/bake_lod_dialog.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://cqmt8f5x5c2ad
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
[gd_scene load_steps=2 format=3 uid="uid://bhvrrmb8bk1bt"]
|
[gd_scene load_steps=2 format=3 uid="uid://bhvrrmb8bk1bt"]
|
||||||
|
|
||||||
[ext_resource type="Script" uid="uid://dft2g3p2pjw12" path="res://addons/terrain_3d/src/bake_lod_dialog.gd" id="1_sf76d"]
|
[ext_resource type="Script" path="res://addons/terrain_3d/menu/bake_lod_dialog.gd" id="1_sf76d"]
|
||||||
|
|
||||||
[node name="bake_lod_dialog" type="ConfirmationDialog"]
|
[node name="bake_lod_dialog" type="ConfirmationDialog"]
|
||||||
title = "Bake Terrain3D Mesh"
|
title = "Bake Terrain3D Mesh"
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
|
# Copyright © 2025 Cory Petkovsek, Roope Palmroos, and Contributors.
|
||||||
|
# Baker for Terrain3D
|
||||||
extends Node
|
extends Node
|
||||||
|
|
||||||
const BakeLodDialog: PackedScene = preload("res://addons/terrain_3d/src/bake_lod_dialog.tscn")
|
const BakeLodDialog: PackedScene = preload("res://addons/terrain_3d/menu/bake_lod_dialog.tscn")
|
||||||
const BAKE_MESH_DESCRIPTION: String = "This will create a child MeshInstance3D. LOD4+ is recommended. LOD0 is slow and dense with vertices every 1 unit. It is not an optimal mesh."
|
const BAKE_MESH_DESCRIPTION: String = "This will create a child MeshInstance3D. LOD4+ is recommended. LOD0 is slow and dense with vertices every 1 unit. It is not an optimal mesh."
|
||||||
const BAKE_OCCLUDER_DESCRIPTION: String = "This will create a child OccluderInstance3D. LOD4+ is recommended and will take 5+ seconds per region to generate. LOD0 is unnecessarily dense and slow."
|
const BAKE_OCCLUDER_DESCRIPTION: String = "This will create a child OccluderInstance3D. LOD4+ is recommended and will take 5+ seconds per region to generate. LOD0 is unnecessarily dense and slow."
|
||||||
const SET_UP_NAVIGATION_DESCRIPTION: String = "This operation will:
|
const SET_UP_NAVIGATION_DESCRIPTION: String = "This operation will:
|
||||||
@@ -194,7 +196,7 @@ func _bake_nav_region_nav_mesh(p_nav_region: NavigationRegion3D) -> void:
|
|||||||
assert(nav_mesh != null)
|
assert(nav_mesh != null)
|
||||||
|
|
||||||
var source_geometry_data := NavigationMeshSourceGeometryData3D.new()
|
var source_geometry_data := NavigationMeshSourceGeometryData3D.new()
|
||||||
NavigationMeshGenerator.parse_source_geometry_data(nav_mesh, source_geometry_data, p_nav_region)
|
NavigationServer3D.parse_source_geometry_data(nav_mesh, source_geometry_data, p_nav_region)
|
||||||
|
|
||||||
for terrain in find_nav_region_terrains(p_nav_region):
|
for terrain in find_nav_region_terrains(p_nav_region):
|
||||||
var aabb: AABB = nav_mesh.filter_baking_aabb
|
var aabb: AABB = nav_mesh.filter_baking_aabb
|
||||||
@@ -204,7 +206,7 @@ func _bake_nav_region_nav_mesh(p_nav_region: NavigationRegion3D) -> void:
|
|||||||
if not faces.is_empty():
|
if not faces.is_empty():
|
||||||
source_geometry_data.add_faces(faces, Transform3D.IDENTITY)
|
source_geometry_data.add_faces(faces, Transform3D.IDENTITY)
|
||||||
|
|
||||||
NavigationMeshGenerator.bake_from_source_geometry_data(nav_mesh, source_geometry_data)
|
NavigationServer3D.bake_from_source_geometry_data(nav_mesh, source_geometry_data)
|
||||||
|
|
||||||
_postprocess_nav_mesh(nav_mesh)
|
_postprocess_nav_mesh(nav_mesh)
|
||||||
|
|
||||||
@@ -374,10 +376,8 @@ func _do_set_up_navigation(p_nav_region: NavigationRegion3D, p_terrain: Terrain3
|
|||||||
var index: int = p_terrain.get_index()
|
var index: int = p_terrain.get_index()
|
||||||
var t_owner: Node = p_terrain.owner
|
var t_owner: Node = p_terrain.owner
|
||||||
|
|
||||||
parent.remove_child(p_terrain)
|
|
||||||
p_nav_region.add_child(p_terrain)
|
|
||||||
|
|
||||||
parent.add_child(p_nav_region, true)
|
parent.add_child(p_nav_region, true)
|
||||||
|
p_terrain.reparent(p_nav_region)
|
||||||
parent.move_child(p_nav_region, index)
|
parent.move_child(p_nav_region, index)
|
||||||
|
|
||||||
p_nav_region.owner = t_owner
|
p_nav_region.owner = t_owner
|
||||||
@@ -391,10 +391,8 @@ func _undo_set_up_navigation(p_nav_region: NavigationRegion3D, p_terrain: Terrai
|
|||||||
var index: int = p_nav_region.get_index()
|
var index: int = p_nav_region.get_index()
|
||||||
var t_owner: Node = p_nav_region.get_owner()
|
var t_owner: Node = p_nav_region.get_owner()
|
||||||
|
|
||||||
|
p_terrain.reparent(parent)
|
||||||
parent.remove_child(p_nav_region)
|
parent.remove_child(p_nav_region)
|
||||||
p_nav_region.remove_child(p_terrain)
|
|
||||||
|
|
||||||
parent.add_child(p_terrain, true)
|
|
||||||
parent.move_child(p_terrain, index)
|
parent.move_child(p_terrain, index)
|
||||||
|
|
||||||
p_terrain.owner = t_owner
|
p_terrain.owner = t_owner
|
||||||
1
addons/terrain_3d/menu/baker.gd.uid
Normal file
1
addons/terrain_3d/menu/baker.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://4ulaeevj5jvi
|
||||||
@@ -1,8 +1,10 @@
|
|||||||
|
# Copyright © 2025 Cory Petkovsek, Roope Palmroos, and Contributors.
|
||||||
|
# Channel Packer for Terrain3D
|
||||||
extends RefCounted
|
extends RefCounted
|
||||||
|
|
||||||
const WINDOW_SCENE: String = "res://addons/terrain_3d/src/channel_packer.tscn"
|
const WINDOW_SCENE: String = "res://addons/terrain_3d/menu/channel_packer.tscn"
|
||||||
const TEMPLATE_PATH: String = "res://addons/terrain_3d/src/channel_packer_import_template.txt"
|
const TEMPLATE_PATH: String = "res://addons/terrain_3d/menu/channel_packer_import_template.txt"
|
||||||
const DRAG_DROP_SCRIPT: String = "res://addons/terrain_3d/src/channel_packer_dragdrop.gd"
|
const DRAG_DROP_SCRIPT: String = "res://addons/terrain_3d/menu/channel_packer_dragdrop.gd"
|
||||||
enum {
|
enum {
|
||||||
INFO,
|
INFO,
|
||||||
WARN,
|
WARN,
|
||||||
@@ -47,7 +49,7 @@ var normal_vector: Vector3
|
|||||||
func pack_textures_popup() -> void:
|
func pack_textures_popup() -> void:
|
||||||
if window != null:
|
if window != null:
|
||||||
window.show()
|
window.show()
|
||||||
window.move_to_foreground()
|
window.grab_focus()
|
||||||
window.move_to_center()
|
window.move_to_center()
|
||||||
return
|
return
|
||||||
window = (load(WINDOW_SCENE) as PackedScene).instantiate()
|
window = (load(WINDOW_SCENE) as PackedScene).instantiate()
|
||||||
@@ -109,8 +111,7 @@ func pack_textures_popup() -> void:
|
|||||||
_init_texture_picker(window.find_child("NormalVBox"), IMAGE_NORMAL)
|
_init_texture_picker(window.find_child("NormalVBox"), IMAGE_NORMAL)
|
||||||
_init_texture_picker(window.find_child("RoughnessVBox"), IMAGE_ROUGHNESS)
|
_init_texture_picker(window.find_child("RoughnessVBox"), IMAGE_ROUGHNESS)
|
||||||
|
|
||||||
var pack_button_path: String = "Panel/MarginContainer/VBoxContainer/PackButton"
|
(window.find_child("PackButton") as Button).pressed.connect(_on_pack_button_pressed)
|
||||||
(window.get_node(pack_button_path) as Button).pressed.connect(_on_pack_button_pressed)
|
|
||||||
|
|
||||||
|
|
||||||
func _on_close_requested() -> void:
|
func _on_close_requested() -> void:
|
||||||
@@ -370,7 +371,7 @@ func _on_save_file_selected(p_dst_path) -> void:
|
|||||||
save_file_dialog.title = "Save Packed Normal/Roughness Texture"
|
save_file_dialog.title = "Save Packed Normal/Roughness Texture"
|
||||||
|
|
||||||
save_file_dialog.call_deferred("popup_centered_ratio")
|
save_file_dialog.call_deferred("popup_centered_ratio")
|
||||||
save_file_dialog.call_deferred("move_to_foreground")
|
save_file_dialog.call_deferred("grab_focus")
|
||||||
|
|
||||||
|
|
||||||
func _alignment_basis(normal: Vector3) -> Basis:
|
func _alignment_basis(normal: Vector3) -> Basis:
|
||||||
@@ -393,8 +394,8 @@ func _set_normal_vector(source: Image, quiet: bool = false) -> void:
|
|||||||
# Calculate texture normal sum direction
|
# Calculate texture normal sum direction
|
||||||
var normal: Image = source
|
var normal: Image = source
|
||||||
var sum: Color = Color(0.0, 0.0, 0.0, 0.0)
|
var sum: Color = Color(0.0, 0.0, 0.0, 0.0)
|
||||||
for x in normal.get_height():
|
for x in normal.get_width():
|
||||||
for y in normal.get_width():
|
for y in normal.get_height():
|
||||||
sum += normal.get_pixel(x, y)
|
sum += normal.get_pixel(x, y)
|
||||||
var div: float = normal.get_height() * normal.get_width()
|
var div: float = normal.get_height() * normal.get_width()
|
||||||
sum /= Color(div, div, div)
|
sum /= Color(div, div, div)
|
||||||
@@ -409,8 +410,8 @@ func _align_normals(source: Image, iteration: int = 0) -> void:
|
|||||||
# generate matrix to re-align the normalmap
|
# generate matrix to re-align the normalmap
|
||||||
var mat3: Basis = _alignment_basis(normal_vector)
|
var mat3: Basis = _alignment_basis(normal_vector)
|
||||||
# re-align the normal map pixels
|
# re-align the normal map pixels
|
||||||
for x in source.get_height():
|
for x in source.get_width():
|
||||||
for y in source.get_width():
|
for y in source.get_height():
|
||||||
var old_pixel: Color = source.get_pixel(x, y)
|
var old_pixel: Color = source.get_pixel(x, y)
|
||||||
var vector_pixel: Vector3 = Vector3(old_pixel.r, old_pixel.g, old_pixel.b)
|
var vector_pixel: Vector3 = Vector3(old_pixel.r, old_pixel.g, old_pixel.b)
|
||||||
vector_pixel *= 2.0
|
vector_pixel *= 2.0
|
||||||
@@ -450,9 +451,8 @@ func _pack_textures(p_rgb_image: Image, p_a_image: Image, p_dst_path: String, p_
|
|||||||
if not output_image:
|
if not output_image:
|
||||||
_show_message(ERROR, "Failed to pack textures")
|
_show_message(ERROR, "Failed to pack textures")
|
||||||
return FAILED
|
return FAILED
|
||||||
if output_image.detect_used_channels() != 5:
|
if output_image.detect_alpha() != Image.ALPHA_BLEND:
|
||||||
_show_message(ERROR, "Packing Error, Alpha Channel empty")
|
_show_message(WARN, "Warning, Alpha channel empty")
|
||||||
return FAILED
|
|
||||||
|
|
||||||
output_image.save_png(p_dst_path)
|
output_image.save_png(p_dst_path)
|
||||||
_create_import_file(p_dst_path)
|
_create_import_file(p_dst_path)
|
||||||
1
addons/terrain_3d/menu/channel_packer.gd.uid
Normal file
1
addons/terrain_3d/menu/channel_packer.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://bwldx4itd58o7
|
||||||
530
addons/terrain_3d/menu/channel_packer.tscn
Normal file
530
addons/terrain_3d/menu/channel_packer.tscn
Normal file
@@ -0,0 +1,530 @@
|
|||||||
|
[gd_scene load_steps=7 format=3 uid="uid://nud6dwjcnj5v"]
|
||||||
|
|
||||||
|
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_ysabf"]
|
||||||
|
bg_color = Color(0.211765, 0.239216, 0.290196, 1)
|
||||||
|
|
||||||
|
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_lcvna"]
|
||||||
|
bg_color = Color(0.168627, 0.211765, 0.266667, 1)
|
||||||
|
border_width_left = 3
|
||||||
|
border_width_top = 3
|
||||||
|
border_width_right = 3
|
||||||
|
border_width_bottom = 3
|
||||||
|
border_color = Color(0.270588, 0.435294, 0.580392, 1)
|
||||||
|
corner_radius_top_left = 5
|
||||||
|
corner_radius_top_right = 5
|
||||||
|
corner_radius_bottom_right = 5
|
||||||
|
corner_radius_bottom_left = 5
|
||||||
|
|
||||||
|
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_cb0xf"]
|
||||||
|
bg_color = Color(0.137255, 0.137255, 0.137255, 1)
|
||||||
|
draw_center = false
|
||||||
|
border_width_left = 3
|
||||||
|
border_width_top = 3
|
||||||
|
border_width_right = 3
|
||||||
|
border_width_bottom = 3
|
||||||
|
border_color = Color(0.784314, 0.784314, 0.784314, 1)
|
||||||
|
corner_radius_top_left = 5
|
||||||
|
corner_radius_top_right = 5
|
||||||
|
corner_radius_bottom_right = 5
|
||||||
|
corner_radius_bottom_left = 5
|
||||||
|
|
||||||
|
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_7qdas"]
|
||||||
|
|
||||||
|
[sub_resource type="ButtonGroup" id="ButtonGroup_wnxik"]
|
||||||
|
|
||||||
|
[sub_resource type="ButtonGroup" id="ButtonGroup_bs6ki"]
|
||||||
|
|
||||||
|
[node name="Window" type="Window"]
|
||||||
|
title = "Terrain3D Channel Packer"
|
||||||
|
initial_position = 1
|
||||||
|
size = Vector2i(583, 856)
|
||||||
|
wrap_controls = true
|
||||||
|
always_on_top = true
|
||||||
|
|
||||||
|
[node name="PanelContainer" type="PanelContainer" parent="."]
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
theme_override_styles/panel = SubResource("StyleBoxFlat_ysabf")
|
||||||
|
|
||||||
|
[node name="MarginContainer" type="MarginContainer" parent="PanelContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_constants/margin_left = 5
|
||||||
|
theme_override_constants/margin_top = 5
|
||||||
|
theme_override_constants/margin_right = 5
|
||||||
|
theme_override_constants/margin_bottom = 5
|
||||||
|
|
||||||
|
[node name="VBoxContainer" type="VBoxContainer" parent="PanelContainer/MarginContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_constants/separation = 10
|
||||||
|
|
||||||
|
[node name="AlbedoHeightPanel" type="PanelContainer" parent="PanelContainer/MarginContainer/VBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_styles/panel = SubResource("StyleBoxFlat_lcvna")
|
||||||
|
|
||||||
|
[node name="MarginContainer" type="MarginContainer" parent="PanelContainer/MarginContainer/VBoxContainer/AlbedoHeightPanel"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_constants/margin_left = 10
|
||||||
|
theme_override_constants/margin_top = 10
|
||||||
|
theme_override_constants/margin_right = 10
|
||||||
|
theme_override_constants/margin_bottom = 10
|
||||||
|
|
||||||
|
[node name="HBoxContainer" type="HBoxContainer" parent="PanelContainer/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="AlbedoVBox" type="VBoxContainer" parent="PanelContainer/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
|
||||||
|
[node name="AlbedoLabel" type="Label" parent="PanelContainer/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/AlbedoVBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
text = "Albedo texture"
|
||||||
|
|
||||||
|
[node name="AlbedoHBox" type="HBoxContainer" parent="PanelContainer/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/AlbedoVBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="LineEdit" type="LineEdit" parent="PanelContainer/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/AlbedoVBox/AlbedoHBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
|
||||||
|
[node name="PickButton" type="Button" parent="PanelContainer/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/AlbedoVBox/AlbedoHBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="ClearButton" type="Button" parent="PanelContainer/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/AlbedoVBox/AlbedoHBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="MarginContainer" type="MarginContainer" parent="PanelContainer/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/AlbedoVBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_vertical = 4
|
||||||
|
theme_override_constants/margin_top = 10
|
||||||
|
|
||||||
|
[node name="Panel" type="Panel" parent="PanelContainer/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/AlbedoVBox/MarginContainer"]
|
||||||
|
custom_minimum_size = Vector2(110, 110)
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 4
|
||||||
|
size_flags_vertical = 4
|
||||||
|
theme_override_styles/panel = SubResource("StyleBoxFlat_cb0xf")
|
||||||
|
|
||||||
|
[node name="TextureRect" type="TextureRect" parent="PanelContainer/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/AlbedoVBox/MarginContainer/Panel"]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 8
|
||||||
|
anchor_left = 0.5
|
||||||
|
anchor_top = 0.5
|
||||||
|
anchor_right = 0.5
|
||||||
|
anchor_bottom = 0.5
|
||||||
|
offset_left = -50.0
|
||||||
|
offset_top = -50.0
|
||||||
|
offset_right = 50.0
|
||||||
|
offset_bottom = 50.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
expand_mode = 1
|
||||||
|
|
||||||
|
[node name="TextureButton" type="Button" parent="PanelContainer/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/AlbedoVBox/MarginContainer/Panel"]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
theme_override_styles/normal = SubResource("StyleBoxEmpty_7qdas")
|
||||||
|
|
||||||
|
[node name="AlbedoWHHBox" type="HBoxContainer" parent="PanelContainer/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/AlbedoVBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
alignment = 1
|
||||||
|
|
||||||
|
[node name="AlbedoW" type="Label" parent="PanelContainer/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/AlbedoVBox/AlbedoWHHBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
horizontal_alignment = 1
|
||||||
|
|
||||||
|
[node name="AlbedoH" type="Label" parent="PanelContainer/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/AlbedoVBox/AlbedoWHHBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
horizontal_alignment = 1
|
||||||
|
|
||||||
|
[node name="HBoxContainer2" type="HBoxContainer" parent="PanelContainer/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/AlbedoVBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
alignment = 1
|
||||||
|
|
||||||
|
[node name="LuminanceAsHeightButton" type="Button" parent="PanelContainer/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/AlbedoVBox/HBoxContainer2"]
|
||||||
|
layout_mode = 2
|
||||||
|
text = " Generate Height from Luminance"
|
||||||
|
icon_alignment = 2
|
||||||
|
|
||||||
|
[node name="HeightVBox" type="VBoxContainer" parent="PanelContainer/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
|
||||||
|
[node name="HeightLabel" type="Label" parent="PanelContainer/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/HeightVBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
text = "Height texture"
|
||||||
|
|
||||||
|
[node name="HeightHBox" type="HBoxContainer" parent="PanelContainer/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/HeightVBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="LineEdit" type="LineEdit" parent="PanelContainer/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/HeightVBox/HeightHBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
|
||||||
|
[node name="PickButton" type="Button" parent="PanelContainer/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/HeightVBox/HeightHBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="ClearButton" type="Button" parent="PanelContainer/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/HeightVBox/HeightHBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="MarginContainer" type="MarginContainer" parent="PanelContainer/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/HeightVBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_vertical = 4
|
||||||
|
theme_override_constants/margin_top = 10
|
||||||
|
|
||||||
|
[node name="Panel" type="Panel" parent="PanelContainer/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/HeightVBox/MarginContainer"]
|
||||||
|
custom_minimum_size = Vector2(110, 110)
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 4
|
||||||
|
size_flags_vertical = 4
|
||||||
|
theme_override_styles/panel = SubResource("StyleBoxFlat_cb0xf")
|
||||||
|
|
||||||
|
[node name="TextureRect" type="TextureRect" parent="PanelContainer/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/HeightVBox/MarginContainer/Panel"]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 8
|
||||||
|
anchor_left = 0.5
|
||||||
|
anchor_top = 0.5
|
||||||
|
anchor_right = 0.5
|
||||||
|
anchor_bottom = 0.5
|
||||||
|
offset_left = -50.0
|
||||||
|
offset_top = -50.0
|
||||||
|
offset_right = 50.0
|
||||||
|
offset_bottom = 50.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
expand_mode = 1
|
||||||
|
|
||||||
|
[node name="TextureButton" type="Button" parent="PanelContainer/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/HeightVBox/MarginContainer/Panel"]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
theme_override_styles/normal = SubResource("StyleBoxEmpty_7qdas")
|
||||||
|
|
||||||
|
[node name="HeightWHHBox" type="HBoxContainer" parent="PanelContainer/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/HeightVBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
alignment = 1
|
||||||
|
|
||||||
|
[node name="HeightW" type="Label" parent="PanelContainer/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/HeightVBox/HeightWHHBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
horizontal_alignment = 1
|
||||||
|
|
||||||
|
[node name="HeightH" type="Label" parent="PanelContainer/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/HeightVBox/HeightWHHBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
horizontal_alignment = 1
|
||||||
|
|
||||||
|
[node name="HBoxContainer2" type="HBoxContainer" parent="PanelContainer/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/HeightVBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
alignment = 1
|
||||||
|
|
||||||
|
[node name="ConvertDepthToHeight" type="CheckBox" parent="PanelContainer/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/HeightVBox/HBoxContainer2"]
|
||||||
|
layout_mode = 2
|
||||||
|
text = " Convert Depth to Height"
|
||||||
|
icon_alignment = 2
|
||||||
|
|
||||||
|
[node name="HBoxContainer" type="HBoxContainer" parent="PanelContainer/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/HeightVBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
alignment = 1
|
||||||
|
|
||||||
|
[node name="HeightChannelLabel" type="Label" parent="PanelContainer/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/HeightVBox/HBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
text = " Source Channel: "
|
||||||
|
horizontal_alignment = 2
|
||||||
|
|
||||||
|
[node name="HeightChannelR" type="Button" parent="PanelContainer/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/HeightVBox/HBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
toggle_mode = true
|
||||||
|
button_pressed = true
|
||||||
|
button_group = SubResource("ButtonGroup_wnxik")
|
||||||
|
text = "R"
|
||||||
|
|
||||||
|
[node name="HeightChannelB" type="Button" parent="PanelContainer/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/HeightVBox/HBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
toggle_mode = true
|
||||||
|
button_group = SubResource("ButtonGroup_wnxik")
|
||||||
|
text = "G"
|
||||||
|
|
||||||
|
[node name="HeightChannelG" type="Button" parent="PanelContainer/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/HeightVBox/HBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
toggle_mode = true
|
||||||
|
button_group = SubResource("ButtonGroup_wnxik")
|
||||||
|
text = "B"
|
||||||
|
|
||||||
|
[node name="HeightChannelA" type="Button" parent="PanelContainer/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/HeightVBox/HBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
toggle_mode = true
|
||||||
|
button_group = SubResource("ButtonGroup_wnxik")
|
||||||
|
text = "A"
|
||||||
|
|
||||||
|
[node name="NormalRoughnessPanel" type="PanelContainer" parent="PanelContainer/MarginContainer/VBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_styles/panel = SubResource("StyleBoxFlat_lcvna")
|
||||||
|
|
||||||
|
[node name="MarginContainer" type="MarginContainer" parent="PanelContainer/MarginContainer/VBoxContainer/NormalRoughnessPanel"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_constants/margin_left = 10
|
||||||
|
theme_override_constants/margin_top = 10
|
||||||
|
theme_override_constants/margin_right = 10
|
||||||
|
theme_override_constants/margin_bottom = 10
|
||||||
|
|
||||||
|
[node name="HBoxContainer" type="HBoxContainer" parent="PanelContainer/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="NormalVBox" type="VBoxContainer" parent="PanelContainer/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
|
||||||
|
[node name="NormalLabel" type="Label" parent="PanelContainer/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/NormalVBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
text = "Normal texture"
|
||||||
|
|
||||||
|
[node name="NormalHBox" type="HBoxContainer" parent="PanelContainer/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/NormalVBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="LineEdit" type="LineEdit" parent="PanelContainer/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/NormalVBox/NormalHBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
|
||||||
|
[node name="PickButton" type="Button" parent="PanelContainer/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/NormalVBox/NormalHBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="ClearButton" type="Button" parent="PanelContainer/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/NormalVBox/NormalHBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="MarginContainer" type="MarginContainer" parent="PanelContainer/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/NormalVBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_vertical = 4
|
||||||
|
theme_override_constants/margin_top = 10
|
||||||
|
|
||||||
|
[node name="Panel" type="Panel" parent="PanelContainer/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/NormalVBox/MarginContainer"]
|
||||||
|
custom_minimum_size = Vector2(110, 110)
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 4
|
||||||
|
size_flags_vertical = 4
|
||||||
|
theme_override_styles/panel = SubResource("StyleBoxFlat_cb0xf")
|
||||||
|
|
||||||
|
[node name="TextureRect" type="TextureRect" parent="PanelContainer/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/NormalVBox/MarginContainer/Panel"]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 8
|
||||||
|
anchor_left = 0.5
|
||||||
|
anchor_top = 0.5
|
||||||
|
anchor_right = 0.5
|
||||||
|
anchor_bottom = 0.5
|
||||||
|
offset_left = -50.0
|
||||||
|
offset_top = -50.0
|
||||||
|
offset_right = 50.0
|
||||||
|
offset_bottom = 50.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
expand_mode = 1
|
||||||
|
|
||||||
|
[node name="TextureButton" type="Button" parent="PanelContainer/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/NormalVBox/MarginContainer/Panel"]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
theme_override_styles/normal = SubResource("StyleBoxEmpty_7qdas")
|
||||||
|
|
||||||
|
[node name="NormalWHHBox" type="HBoxContainer" parent="PanelContainer/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/NormalVBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
alignment = 1
|
||||||
|
|
||||||
|
[node name="NormalW" type="Label" parent="PanelContainer/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/NormalVBox/NormalWHHBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
horizontal_alignment = 1
|
||||||
|
|
||||||
|
[node name="NormalH" type="Label" parent="PanelContainer/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/NormalVBox/NormalWHHBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
horizontal_alignment = 1
|
||||||
|
|
||||||
|
[node name="HBoxContainer" type="HBoxContainer" parent="PanelContainer/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/NormalVBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
alignment = 1
|
||||||
|
|
||||||
|
[node name="InvertGreenChannelCheckBox" type="CheckBox" parent="PanelContainer/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/NormalVBox/HBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
text = " Convert DirectX to OpenGL"
|
||||||
|
|
||||||
|
[node name="HBoxContainer2" type="HBoxContainer" parent="PanelContainer/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/NormalVBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
alignment = 1
|
||||||
|
|
||||||
|
[node name="AlignNormalsCheckBox" type="CheckBox" parent="PanelContainer/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/NormalVBox/HBoxContainer2"]
|
||||||
|
layout_mode = 2
|
||||||
|
text = " Orthoganolise Normals"
|
||||||
|
|
||||||
|
[node name="RoughnessVBox" type="VBoxContainer" parent="PanelContainer/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
|
||||||
|
[node name="RoughnessLabel" type="Label" parent="PanelContainer/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/RoughnessVBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
text = "Roughness texture"
|
||||||
|
|
||||||
|
[node name="RoughnessHBox" type="HBoxContainer" parent="PanelContainer/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/RoughnessVBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="LineEdit" type="LineEdit" parent="PanelContainer/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/RoughnessVBox/RoughnessHBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
|
||||||
|
[node name="PickButton" type="Button" parent="PanelContainer/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/RoughnessVBox/RoughnessHBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="ClearButton" type="Button" parent="PanelContainer/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/RoughnessVBox/RoughnessHBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="MarginContainer" type="MarginContainer" parent="PanelContainer/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/RoughnessVBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_vertical = 4
|
||||||
|
theme_override_constants/margin_top = 10
|
||||||
|
|
||||||
|
[node name="Panel" type="Panel" parent="PanelContainer/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/RoughnessVBox/MarginContainer"]
|
||||||
|
custom_minimum_size = Vector2(110, 110)
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 4
|
||||||
|
size_flags_vertical = 4
|
||||||
|
theme_override_styles/panel = SubResource("StyleBoxFlat_cb0xf")
|
||||||
|
|
||||||
|
[node name="TextureRect" type="TextureRect" parent="PanelContainer/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/RoughnessVBox/MarginContainer/Panel"]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 8
|
||||||
|
anchor_left = 0.5
|
||||||
|
anchor_top = 0.5
|
||||||
|
anchor_right = 0.5
|
||||||
|
anchor_bottom = 0.5
|
||||||
|
offset_left = -50.0
|
||||||
|
offset_top = -50.0
|
||||||
|
offset_right = 50.0
|
||||||
|
offset_bottom = 50.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
expand_mode = 1
|
||||||
|
|
||||||
|
[node name="TextureButton" type="Button" parent="PanelContainer/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/RoughnessVBox/MarginContainer/Panel"]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
theme_override_styles/normal = SubResource("StyleBoxEmpty_7qdas")
|
||||||
|
|
||||||
|
[node name="RoughnessWHHBox" type="HBoxContainer" parent="PanelContainer/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/RoughnessVBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
alignment = 1
|
||||||
|
|
||||||
|
[node name="RoughnessW" type="Label" parent="PanelContainer/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/RoughnessVBox/RoughnessWHHBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
horizontal_alignment = 1
|
||||||
|
|
||||||
|
[node name="RoughnessH" type="Label" parent="PanelContainer/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/RoughnessVBox/RoughnessWHHBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
horizontal_alignment = 1
|
||||||
|
|
||||||
|
[node name="HBoxContainer2" type="HBoxContainer" parent="PanelContainer/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/RoughnessVBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
alignment = 1
|
||||||
|
|
||||||
|
[node name="InvertSmoothCheckBox" type="CheckBox" parent="PanelContainer/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/RoughnessVBox/HBoxContainer2"]
|
||||||
|
layout_mode = 2
|
||||||
|
text = " Convert Smoothness to Roughness"
|
||||||
|
|
||||||
|
[node name="HBoxContainer" type="HBoxContainer" parent="PanelContainer/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/RoughnessVBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
alignment = 1
|
||||||
|
|
||||||
|
[node name="RoughnessChannelLabel" type="Label" parent="PanelContainer/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/RoughnessVBox/HBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
text = " Source Channel: "
|
||||||
|
horizontal_alignment = 2
|
||||||
|
|
||||||
|
[node name="RoughnessChannelR" type="Button" parent="PanelContainer/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/RoughnessVBox/HBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
toggle_mode = true
|
||||||
|
button_pressed = true
|
||||||
|
button_group = SubResource("ButtonGroup_bs6ki")
|
||||||
|
text = "R"
|
||||||
|
|
||||||
|
[node name="RoughnessChannelG" type="Button" parent="PanelContainer/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/RoughnessVBox/HBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
toggle_mode = true
|
||||||
|
button_group = SubResource("ButtonGroup_bs6ki")
|
||||||
|
text = "G"
|
||||||
|
|
||||||
|
[node name="RoughnessChannelB" type="Button" parent="PanelContainer/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/RoughnessVBox/HBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
toggle_mode = true
|
||||||
|
button_group = SubResource("ButtonGroup_bs6ki")
|
||||||
|
text = "B"
|
||||||
|
|
||||||
|
[node name="RoughnessChannelA" type="Button" parent="PanelContainer/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/RoughnessVBox/HBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
toggle_mode = true
|
||||||
|
button_group = SubResource("ButtonGroup_bs6ki")
|
||||||
|
text = "A"
|
||||||
|
|
||||||
|
[node name="GeneralOptionsLabel" type="Label" parent="PanelContainer/MarginContainer/VBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
text = "General Options"
|
||||||
|
horizontal_alignment = 1
|
||||||
|
vertical_alignment = 1
|
||||||
|
|
||||||
|
[node name="GeneralOptionsHBox" type="HBoxContainer" parent="PanelContainer/MarginContainer/VBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
alignment = 1
|
||||||
|
|
||||||
|
[node name="ResizeToggle" type="CheckBox" parent="PanelContainer/MarginContainer/VBoxContainer/GeneralOptionsHBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
text = " Resize Packed Image"
|
||||||
|
|
||||||
|
[node name="ResizeOptionButton" type="SpinBox" parent="PanelContainer/MarginContainer/VBoxContainer/GeneralOptionsHBox"]
|
||||||
|
visible = false
|
||||||
|
layout_mode = 2
|
||||||
|
tooltip_text = "A value of 0 disables resizing."
|
||||||
|
min_value = 128.0
|
||||||
|
max_value = 4096.0
|
||||||
|
step = 128.0
|
||||||
|
value = 1024.0
|
||||||
|
|
||||||
|
[node name="VSeparator" type="VSeparator" parent="PanelContainer/MarginContainer/VBoxContainer/GeneralOptionsHBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="GenerateMipmapsCheckBox" type="CheckBox" parent="PanelContainer/MarginContainer/VBoxContainer/GeneralOptionsHBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
button_pressed = true
|
||||||
|
text = "Generate Mipmaps"
|
||||||
|
|
||||||
|
[node name="HighQualityCheckBox" type="CheckBox" parent="PanelContainer/MarginContainer/VBoxContainer/GeneralOptionsHBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
text = "Import High Quality"
|
||||||
|
|
||||||
|
[node name="PackButton" type="Button" parent="PanelContainer/MarginContainer/VBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
text = "Pack textures as..."
|
||||||
|
|
||||||
|
[node name="StatusLabel" type="Label" parent="PanelContainer/MarginContainer/VBoxContainer"]
|
||||||
|
custom_minimum_size = Vector2(0, 60)
|
||||||
|
layout_mode = 2
|
||||||
|
horizontal_alignment = 1
|
||||||
|
autowrap_mode = 3
|
||||||
|
|
||||||
|
[node name="HBoxContainer" type="HBoxContainer" parent="PanelContainer/MarginContainer/VBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
alignment = 1
|
||||||
|
|
||||||
|
[node name="CloseButton" type="Button" parent="PanelContainer/MarginContainer/VBoxContainer/HBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
text = "Close"
|
||||||
|
|
||||||
|
[connection signal="toggled" from="PanelContainer/MarginContainer/VBoxContainer/GeneralOptionsHBox/ResizeToggle" to="PanelContainer/MarginContainer/VBoxContainer/GeneralOptionsHBox/ResizeOptionButton" method="set_visible"]
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
# Copyright © 2025 Cory Petkovsek, Roope Palmroos, and Contributors.
|
||||||
|
# Channel Packer Dragdropper for Terrain3D
|
||||||
@tool
|
@tool
|
||||||
extends Button
|
extends Button
|
||||||
|
|
||||||
1
addons/terrain_3d/menu/channel_packer_dragdrop.gd.uid
Normal file
1
addons/terrain_3d/menu/channel_packer_dragdrop.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://br45krrqbw8bg
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
|
# Copyright © 2025 Cory Petkovsek, Roope Palmroos, and Contributors.
|
||||||
|
# Directory Setup for Terrain3D
|
||||||
extends Node
|
extends Node
|
||||||
|
|
||||||
const DIRECTORY_SETUP: String = "res://addons/terrain_3d/src/directory_setup.tscn"
|
const DIRECTORY_SETUP: String = "res://addons/terrain_3d/menu/directory_setup.tscn"
|
||||||
|
|
||||||
var plugin: EditorPlugin
|
var plugin: EditorPlugin
|
||||||
var dialog: ConfirmationDialog
|
var dialog: ConfirmationDialog
|
||||||
var select_dir_btn: Button
|
var select_dir_btn: Button
|
||||||
var selected_dir_le: LineEdit
|
var selected_dir_le: LineEdit
|
||||||
var select_upg_btn: Button
|
|
||||||
var upgrade_file_le: LineEdit
|
|
||||||
var editor_file_dialog: EditorFileDialog
|
var editor_file_dialog: EditorFileDialog
|
||||||
|
|
||||||
|
|
||||||
@@ -18,7 +18,6 @@ func _init() -> void:
|
|||||||
editor_file_dialog.access = EditorFileDialog.ACCESS_RESOURCES
|
editor_file_dialog.access = EditorFileDialog.ACCESS_RESOURCES
|
||||||
editor_file_dialog.ok_button_text = "Open"
|
editor_file_dialog.ok_button_text = "Open"
|
||||||
editor_file_dialog.title = "Open a folder or file"
|
editor_file_dialog.title = "Open a folder or file"
|
||||||
editor_file_dialog.file_selected.connect(_on_file_selected)
|
|
||||||
editor_file_dialog.dir_selected.connect(_on_dir_selected)
|
editor_file_dialog.dir_selected.connect(_on_dir_selected)
|
||||||
editor_file_dialog.size = Vector2i(850, 550)
|
editor_file_dialog.size = Vector2i(850, 550)
|
||||||
editor_file_dialog.transient = false
|
editor_file_dialog.transient = false
|
||||||
@@ -34,22 +33,14 @@ func directory_setup_popup() -> void:
|
|||||||
# Nodes
|
# Nodes
|
||||||
select_dir_btn = dialog.get_node("Margin/VBox/DirHBox/SelectDir")
|
select_dir_btn = dialog.get_node("Margin/VBox/DirHBox/SelectDir")
|
||||||
selected_dir_le = dialog.get_node("Margin/VBox/DirHBox/LineEdit")
|
selected_dir_le = dialog.get_node("Margin/VBox/DirHBox/LineEdit")
|
||||||
select_upg_btn = dialog.get_node("Margin/VBox/UpgradeHBox/SelectResFile")
|
|
||||||
upgrade_file_le = dialog.get_node("Margin/VBox/UpgradeHBox/LineEdit")
|
|
||||||
upgrade_file_le.text = ""
|
|
||||||
|
|
||||||
if plugin.terrain.data_directory:
|
if plugin.terrain.data_directory:
|
||||||
selected_dir_le.text = plugin.terrain.data_directory
|
selected_dir_le.text = plugin.terrain.data_directory
|
||||||
|
|
||||||
if plugin.terrain.storage:
|
|
||||||
upgrade_file_le.text = plugin.terrain.storage.get_path()
|
|
||||||
|
|
||||||
# Icons
|
# Icons
|
||||||
plugin.ui.set_button_editor_icon(select_upg_btn, "Folder")
|
|
||||||
plugin.ui.set_button_editor_icon(select_dir_btn, "Folder")
|
plugin.ui.set_button_editor_icon(select_dir_btn, "Folder")
|
||||||
|
|
||||||
#Signals
|
#Signals
|
||||||
select_upg_btn.pressed.connect(_on_select_file_pressed.bind(EditorFileDialog.FILE_MODE_OPEN_FILE))
|
|
||||||
select_dir_btn.pressed.connect(_on_select_file_pressed.bind(EditorFileDialog.FILE_MODE_OPEN_DIR))
|
select_dir_btn.pressed.connect(_on_select_file_pressed.bind(EditorFileDialog.FILE_MODE_OPEN_DIR))
|
||||||
dialog.confirmed.connect(_on_close_requested)
|
dialog.confirmed.connect(_on_close_requested)
|
||||||
dialog.canceled.connect(_on_close_requested)
|
dialog.canceled.connect(_on_close_requested)
|
||||||
@@ -73,10 +64,6 @@ func _on_dir_selected(path: String) -> void:
|
|||||||
selected_dir_le.text = path
|
selected_dir_le.text = path
|
||||||
|
|
||||||
|
|
||||||
func _on_file_selected(path: String) -> void:
|
|
||||||
upgrade_file_le.text = path
|
|
||||||
|
|
||||||
|
|
||||||
func _on_ok_pressed() -> void:
|
func _on_ok_pressed() -> void:
|
||||||
if not plugin.terrain:
|
if not plugin.terrain:
|
||||||
push_error("Not connected terrain. Click the Terrain3D node first")
|
push_error("Not connected terrain. Click the Terrain3D node first")
|
||||||
@@ -97,20 +84,3 @@ func _on_ok_pressed() -> void:
|
|||||||
|
|
||||||
print("Setting terrain directory: ", selected_dir_le.text)
|
print("Setting terrain directory: ", selected_dir_le.text)
|
||||||
plugin.terrain.data_directory = selected_dir_le.text
|
plugin.terrain.data_directory = selected_dir_le.text
|
||||||
|
|
||||||
if not upgrade_file_le.text.is_empty():
|
|
||||||
if data_found:
|
|
||||||
push_warning("Target directory already has terrain data. Specify an empty directory to upgrade")
|
|
||||||
return
|
|
||||||
if not FileAccess.file_exists(upgrade_file_le.text):
|
|
||||||
push_error("File doesn't exist: ", upgrade_file_le.text)
|
|
||||||
return
|
|
||||||
|
|
||||||
if not plugin.terrain.storage or \
|
|
||||||
( plugin.terrain.storage and plugin.terrain.storage.get_path() != upgrade_file_le.text):
|
|
||||||
print("Loading storage file: ", upgrade_file_le.text)
|
|
||||||
plugin.terrain.set_storage(load(upgrade_file_le.text))
|
|
||||||
|
|
||||||
if plugin.terrain.storage:
|
|
||||||
print("Begining upgrade of: ", upgrade_file_le.text)
|
|
||||||
plugin.terrain.split_storage()
|
|
||||||
1
addons/terrain_3d/menu/directory_setup.gd.uid
Normal file
1
addons/terrain_3d/menu/directory_setup.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://0034ukv2mngn
|
||||||
@@ -3,14 +3,14 @@
|
|||||||
[node name="DirectorySetup" type="ConfirmationDialog"]
|
[node name="DirectorySetup" type="ConfirmationDialog"]
|
||||||
title = "Terrain3D Data Directory Setup"
|
title = "Terrain3D Data Directory Setup"
|
||||||
position = Vector2i(0, 36)
|
position = Vector2i(0, 36)
|
||||||
size = Vector2i(750, 574)
|
size = Vector2i(750, 330)
|
||||||
visible = true
|
visible = true
|
||||||
|
|
||||||
[node name="Margin" type="MarginContainer" parent="."]
|
[node name="Margin" type="MarginContainer" parent="."]
|
||||||
offset_left = 8.0
|
offset_left = 8.0
|
||||||
offset_top = 8.0
|
offset_top = 8.0
|
||||||
offset_right = 742.0
|
offset_right = 742.0
|
||||||
offset_bottom = 525.0
|
offset_bottom = 281.0
|
||||||
theme_override_constants/margin_left = 20
|
theme_override_constants/margin_left = 20
|
||||||
theme_override_constants/margin_top = 20
|
theme_override_constants/margin_top = 20
|
||||||
theme_override_constants/margin_right = 20
|
theme_override_constants/margin_right = 20
|
||||||
@@ -43,24 +43,6 @@ placeholder_text = "Data directory"
|
|||||||
[node name="SelectDir" type="Button" parent="Margin/VBox/DirHBox"]
|
[node name="SelectDir" type="Button" parent="Margin/VBox/DirHBox"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
|
||||||
[node name="UpgradeLabel" type="Label" parent="Margin/VBox"]
|
|
||||||
custom_minimum_size = Vector2(400, 0)
|
|
||||||
layout_mode = 2
|
|
||||||
text = "
|
|
||||||
If you wish to upgrade a storage file from v0.8.4 - v0.9.2, specify it below. Data will be stored in the directory above upon save. The original file will not be touched."
|
|
||||||
autowrap_mode = 3
|
|
||||||
|
|
||||||
[node name="UpgradeHBox" type="HBoxContainer" parent="Margin/VBox"]
|
|
||||||
layout_mode = 2
|
|
||||||
|
|
||||||
[node name="LineEdit" type="LineEdit" parent="Margin/VBox/UpgradeHBox"]
|
|
||||||
layout_mode = 2
|
|
||||||
size_flags_horizontal = 3
|
|
||||||
placeholder_text = "Storage .res to upgrade"
|
|
||||||
|
|
||||||
[node name="SelectResFile" type="Button" parent="Margin/VBox/UpgradeHBox"]
|
|
||||||
layout_mode = 2
|
|
||||||
|
|
||||||
[node name="Spacer" type="Control" parent="Margin/VBox"]
|
[node name="Spacer" type="Control" parent="Margin/VBox"]
|
||||||
custom_minimum_size = Vector2(0, 40)
|
custom_minimum_size = Vector2(0, 40)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
|
# Copyright © 2025 Cory Petkovsek, Roope Palmroos, and Contributors.
|
||||||
|
# Menu for Terrain3D
|
||||||
extends HBoxContainer
|
extends HBoxContainer
|
||||||
|
|
||||||
|
|
||||||
const DirectoryWizard: Script = preload("res://addons/terrain_3d/src/directory_setup.gd")
|
const DirectoryWizard: Script = preload("res://addons/terrain_3d/menu/directory_setup.gd")
|
||||||
const Packer: Script = preload("res://addons/terrain_3d/src/channel_packer.gd")
|
const Packer: Script = preload("res://addons/terrain_3d/menu/channel_packer.gd")
|
||||||
const Baker: Script = preload("res://addons/terrain_3d/src/baker.gd")
|
const Baker: Script = preload("res://addons/terrain_3d/menu/baker.gd")
|
||||||
|
|
||||||
var plugin: EditorPlugin
|
var plugin: EditorPlugin
|
||||||
var menu_button: MenuButton = MenuButton.new()
|
var menu_button: MenuButton = MenuButton.new()
|
||||||
@@ -31,7 +33,7 @@ func _enter_tree() -> void:
|
|||||||
add_child(directory_setup)
|
add_child(directory_setup)
|
||||||
add_child(baker)
|
add_child(baker)
|
||||||
|
|
||||||
menu_button.text = "Terrain3D Tools"
|
menu_button.text = "Terrain3D"
|
||||||
menu_button.get_popup().add_item("Directory Setup...", MENU_DIRECTORY_SETUP)
|
menu_button.get_popup().add_item("Directory Setup...", MENU_DIRECTORY_SETUP)
|
||||||
menu_button.get_popup().add_item("Pack Textures...", MENU_PACK_TEXTURES)
|
menu_button.get_popup().add_item("Pack Textures...", MENU_PACK_TEXTURES)
|
||||||
menu_button.get_popup().add_separator("", MENU_SEPARATOR)
|
menu_button.get_popup().add_separator("", MENU_SEPARATOR)
|
||||||
1
addons/terrain_3d/menu/terrain_menu.gd.uid
Normal file
1
addons/terrain_3d/menu/terrain_menu.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://3gxvahogxa10
|
||||||
@@ -3,5 +3,5 @@
|
|||||||
name="Terrain3D"
|
name="Terrain3D"
|
||||||
description="A high performance, editable terrain system for Godot 4."
|
description="A high performance, editable terrain system for Godot 4."
|
||||||
author="Cory Petkovsek & Roope Palmroos"
|
author="Cory Petkovsek & Roope Palmroos"
|
||||||
version="0.9.3a"
|
version="1.0.0"
|
||||||
script="editor.gd"
|
script="src/editor_plugin.gd"
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
|
# Copyright © 2025 Cory Petkovsek, Roope Palmroos, and Contributors.
|
||||||
|
# Asset Dock for Terrain3D
|
||||||
@tool
|
@tool
|
||||||
extends PanelContainer
|
extends PanelContainer
|
||||||
#class_name Terrain3DAssetDock
|
|
||||||
|
|
||||||
signal confirmation_closed
|
signal confirmation_closed
|
||||||
signal confirmation_confirmed
|
signal confirmation_confirmed
|
||||||
@@ -444,9 +445,6 @@ class ListContainer extends Container:
|
|||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
set_v_size_flags(SIZE_EXPAND_FILL)
|
set_v_size_flags(SIZE_EXPAND_FILL)
|
||||||
set_h_size_flags(SIZE_EXPAND_FILL)
|
set_h_size_flags(SIZE_EXPAND_FILL)
|
||||||
focus_style = get_theme_stylebox("focus", "Button").duplicate()
|
|
||||||
focus_style.set_border_width_all(2)
|
|
||||||
focus_style.set_border_color(Color(1, 1, 1, .67))
|
|
||||||
|
|
||||||
|
|
||||||
func clear() -> void:
|
func clear() -> void:
|
||||||
@@ -588,17 +586,13 @@ class ListContainer extends Container:
|
|||||||
last_offset = 3
|
last_offset = 3
|
||||||
set_selected_id(clamp(selected_id, 0, entries.size() - last_offset))
|
set_selected_id(clamp(selected_id, 0, entries.size() - last_offset))
|
||||||
|
|
||||||
# Update editor with selected brush
|
|
||||||
plugin.ui._on_setting_changed()
|
|
||||||
|
|
||||||
|
|
||||||
func get_selected_id() -> int:
|
func get_selected_id() -> int:
|
||||||
return selected_id
|
return selected_id
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
func set_entry_width(value: float) -> void:
|
func set_entry_width(value: float) -> void:
|
||||||
width = clamp(value, 56, 230)
|
width = clamp(value, 66, 230)
|
||||||
redraw()
|
redraw()
|
||||||
|
|
||||||
|
|
||||||
@@ -651,45 +645,86 @@ class ListEntry extends VBoxContainer:
|
|||||||
var is_selected: bool = false
|
var is_selected: bool = false
|
||||||
var asset_list: Terrain3DAssets
|
var asset_list: Terrain3DAssets
|
||||||
|
|
||||||
var button_clear: TextureButton
|
@onready var button_row := HBoxContainer.new()
|
||||||
var button_edit: TextureButton
|
@onready var button_clear := TextureButton.new()
|
||||||
var name_label: Label
|
@onready var button_edit := TextureButton.new()
|
||||||
|
@onready var spacer := Control.new()
|
||||||
@onready var add_icon: Texture2D = get_theme_icon("Add", "EditorIcons")
|
@onready var button_enabled := TextureButton.new()
|
||||||
@onready var clear_icon: Texture2D = get_theme_icon("Close", "EditorIcons")
|
@onready var clear_icon: Texture2D = get_theme_icon("Close", "EditorIcons")
|
||||||
@onready var edit_icon: Texture2D = get_theme_icon("Edit", "EditorIcons")
|
@onready var edit_icon: Texture2D = get_theme_icon("Edit", "EditorIcons")
|
||||||
|
@onready var enabled_icon: Texture2D = get_theme_icon("GuiVisibilityVisible", "EditorIcons")
|
||||||
|
@onready var disabled_icon: Texture2D = get_theme_icon("GuiVisibilityHidden", "EditorIcons")
|
||||||
|
|
||||||
|
var name_label: Label
|
||||||
|
@onready var add_icon: Texture2D = get_theme_icon("Add", "EditorIcons")
|
||||||
@onready var background: StyleBox = get_theme_stylebox("pressed", "Button")
|
@onready var background: StyleBox = get_theme_stylebox("pressed", "Button")
|
||||||
var focus_style: StyleBox
|
@onready var focus_style: StyleBox = get_theme_stylebox("focus", "Button").duplicate()
|
||||||
|
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
|
setup_buttons()
|
||||||
|
setup_label()
|
||||||
|
focus_style.set_border_width_all(2)
|
||||||
|
focus_style.set_border_color(Color(1, 1, 1, .67))
|
||||||
|
|
||||||
|
|
||||||
|
func setup_buttons() -> void:
|
||||||
var icon_size: Vector2 = Vector2(12, 12)
|
var icon_size: Vector2 = Vector2(12, 12)
|
||||||
|
var margin_container := MarginContainer.new()
|
||||||
|
margin_container.mouse_filter = Control.MOUSE_FILTER_PASS
|
||||||
|
margin_container.add_theme_constant_override("margin_top", 5)
|
||||||
|
margin_container.add_theme_constant_override("margin_left", 5)
|
||||||
|
margin_container.add_theme_constant_override("margin_right", 5)
|
||||||
|
add_child(margin_container)
|
||||||
|
|
||||||
button_clear = TextureButton.new()
|
button_row.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||||
button_clear.set_texture_normal(clear_icon)
|
button_row.alignment = BoxContainer.ALIGNMENT_CENTER
|
||||||
button_clear.set_custom_minimum_size(icon_size)
|
button_row.mouse_filter = Control.MOUSE_FILTER_PASS
|
||||||
button_clear.set_h_size_flags(Control.SIZE_SHRINK_END)
|
margin_container.add_child(button_row)
|
||||||
button_clear.set_visible(resource != null)
|
|
||||||
button_clear.pressed.connect(clear)
|
if type == Terrain3DAssets.TYPE_MESH:
|
||||||
add_child(button_clear)
|
button_enabled.set_texture_normal(enabled_icon)
|
||||||
|
button_enabled.set_texture_pressed(disabled_icon)
|
||||||
|
button_enabled.set_custom_minimum_size(icon_size)
|
||||||
|
button_enabled.set_h_size_flags(Control.SIZE_SHRINK_END)
|
||||||
|
button_enabled.set_visible(resource != null)
|
||||||
|
button_enabled.toggle_mode = true
|
||||||
|
button_enabled.mouse_filter = Control.MOUSE_FILTER_PASS
|
||||||
|
button_enabled.pressed.connect(enable)
|
||||||
|
button_row.add_child(button_enabled)
|
||||||
|
|
||||||
button_edit = TextureButton.new()
|
|
||||||
button_edit.set_texture_normal(edit_icon)
|
button_edit.set_texture_normal(edit_icon)
|
||||||
button_edit.set_custom_minimum_size(icon_size)
|
button_edit.set_custom_minimum_size(icon_size)
|
||||||
button_edit.set_h_size_flags(Control.SIZE_SHRINK_END)
|
button_edit.set_h_size_flags(Control.SIZE_SHRINK_END)
|
||||||
button_edit.set_visible(resource != null)
|
button_edit.set_visible(resource != null)
|
||||||
|
button_edit.mouse_filter = Control.MOUSE_FILTER_PASS
|
||||||
button_edit.pressed.connect(edit)
|
button_edit.pressed.connect(edit)
|
||||||
add_child(button_edit)
|
button_row.add_child(button_edit)
|
||||||
|
|
||||||
|
spacer.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||||
|
spacer.mouse_filter = Control.MOUSE_FILTER_PASS
|
||||||
|
button_row.add_child(spacer)
|
||||||
|
|
||||||
|
button_clear.set_texture_normal(clear_icon)
|
||||||
|
button_clear.set_custom_minimum_size(icon_size)
|
||||||
|
button_clear.set_h_size_flags(Control.SIZE_SHRINK_END)
|
||||||
|
button_clear.set_visible(resource != null)
|
||||||
|
button_clear.mouse_filter = Control.MOUSE_FILTER_PASS
|
||||||
|
button_clear.pressed.connect(clear)
|
||||||
|
button_row.add_child(button_clear)
|
||||||
|
|
||||||
|
|
||||||
|
func setup_label() -> void:
|
||||||
name_label = Label.new()
|
name_label = Label.new()
|
||||||
add_child(name_label, true)
|
add_child(name_label, true)
|
||||||
name_label.visible = false
|
name_label.visible = false
|
||||||
name_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
name_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||||
name_label.vertical_alignment = VERTICAL_ALIGNMENT_BOTTOM
|
name_label.vertical_alignment = VERTICAL_ALIGNMENT_BOTTOM
|
||||||
name_label.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
name_label.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||||
|
name_label.add_theme_color_override("font_color", Color.WHITE)
|
||||||
name_label.add_theme_color_override("font_shadow_color", Color.BLACK)
|
name_label.add_theme_color_override("font_shadow_color", Color.BLACK)
|
||||||
name_label.add_theme_constant_override("shadow_offset_x", 1)
|
name_label.add_theme_constant_override("shadow_offset_x", 1.)
|
||||||
name_label.add_theme_constant_override("shadow_offset_y", 1)
|
name_label.add_theme_constant_override("shadow_offset_y", 1.)
|
||||||
name_label.add_theme_font_size_override("font_size", 15)
|
name_label.add_theme_font_size_override("font_size", 15)
|
||||||
name_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
name_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||||
name_label.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS
|
name_label.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS
|
||||||
@@ -702,6 +737,9 @@ class ListEntry extends VBoxContainer:
|
|||||||
func _notification(p_what) -> void:
|
func _notification(p_what) -> void:
|
||||||
match p_what:
|
match p_what:
|
||||||
NOTIFICATION_DRAW:
|
NOTIFICATION_DRAW:
|
||||||
|
# Hide spacer if icons are crowding small textures
|
||||||
|
spacer.visible = size.x > 70 or type == Terrain3DAssets.TYPE_TEXTURE
|
||||||
|
|
||||||
var rect: Rect2 = Rect2(Vector2.ZERO, get_size())
|
var rect: Rect2 = Rect2(Vector2.ZERO, get_size())
|
||||||
if !resource:
|
if !resource:
|
||||||
draw_style_box(background, rect)
|
draw_style_box(background, rect)
|
||||||
@@ -723,6 +761,7 @@ class ListEntry extends VBoxContainer:
|
|||||||
texture_filter = CanvasItem.TEXTURE_FILTER_LINEAR_WITH_MIPMAPS
|
texture_filter = CanvasItem.TEXTURE_FILTER_LINEAR_WITH_MIPMAPS
|
||||||
else:
|
else:
|
||||||
draw_rect(rect, Color(.15, .15, .15, 1.))
|
draw_rect(rect, Color(.15, .15, .15, 1.))
|
||||||
|
button_enabled.set_pressed_no_signal(!resource.is_enabled())
|
||||||
name_label.add_theme_font_size_override("font_size", 4 + rect.size.x/10)
|
name_label.add_theme_font_size_override("font_size", 4 + rect.size.x/10)
|
||||||
if drop_data:
|
if drop_data:
|
||||||
draw_style_box(focus_style, rect)
|
draw_style_box(focus_style, rect)
|
||||||
@@ -791,8 +830,8 @@ class ListEntry extends VBoxContainer:
|
|||||||
var ma := Terrain3DMeshAsset.new()
|
var ma := Terrain3DMeshAsset.new()
|
||||||
if resource is Terrain3DMeshAsset:
|
if resource is Terrain3DMeshAsset:
|
||||||
ma.id = resource.id
|
ma.id = resource.id
|
||||||
ma.set_scene_file(res)
|
|
||||||
set_edited_resource(ma, false)
|
set_edited_resource(ma, false)
|
||||||
|
ma.set_scene_file(res)
|
||||||
resource = ma
|
resource = ma
|
||||||
elif res is Terrain3DMeshAsset and type == Terrain3DAssets.TYPE_MESH:
|
elif res is Terrain3DMeshAsset and type == Terrain3DAssets.TYPE_MESH:
|
||||||
if resource is Terrain3DMeshAsset:
|
if resource is Terrain3DMeshAsset:
|
||||||
@@ -808,6 +847,8 @@ class ListEntry extends VBoxContainer:
|
|||||||
if resource:
|
if resource:
|
||||||
resource.setting_changed.connect(_on_resource_changed)
|
resource.setting_changed.connect(_on_resource_changed)
|
||||||
resource.file_changed.connect(_on_resource_changed)
|
resource.file_changed.connect(_on_resource_changed)
|
||||||
|
if resource is Terrain3DMeshAsset:
|
||||||
|
resource.instancer_setting_changed.connect(_on_resource_changed)
|
||||||
|
|
||||||
if button_clear:
|
if button_clear:
|
||||||
button_clear.set_visible(resource != null)
|
button_clear.set_visible(resource != null)
|
||||||
@@ -818,6 +859,7 @@ class ListEntry extends VBoxContainer:
|
|||||||
|
|
||||||
|
|
||||||
func _on_resource_changed() -> void:
|
func _on_resource_changed() -> void:
|
||||||
|
queue_redraw()
|
||||||
emit_signal("changed", resource)
|
emit_signal("changed", resource)
|
||||||
|
|
||||||
|
|
||||||
@@ -834,3 +876,8 @@ class ListEntry extends VBoxContainer:
|
|||||||
func edit() -> void:
|
func edit() -> void:
|
||||||
emit_signal("selected")
|
emit_signal("selected")
|
||||||
emit_signal("inspected", resource)
|
emit_signal("inspected", resource)
|
||||||
|
|
||||||
|
|
||||||
|
func enable() -> void:
|
||||||
|
if resource is Terrain3DMeshAsset:
|
||||||
|
resource.set_enabled(!resource.is_enabled())
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
uid://bcfr30tpffi5o
|
uid://bgoifepft1hjw
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[gd_scene load_steps=2 format=3 uid="uid://dkb6hii5e48m2"]
|
[gd_scene load_steps=2 format=3 uid="uid://dkb6hii5e48m2"]
|
||||||
|
|
||||||
[ext_resource type="Script" uid="uid://bcfr30tpffi5o" path="res://addons/terrain_3d/src/asset_dock.gd" id="1_e23pg"]
|
[ext_resource type="Script" path="res://addons/terrain_3d/src/asset_dock.gd" id="1_e23pg"]
|
||||||
|
|
||||||
[node name="Terrain3D" type="PanelContainer"]
|
[node name="Terrain3D" type="PanelContainer"]
|
||||||
custom_minimum_size = Vector2(256, 95)
|
custom_minimum_size = Vector2(256, 95)
|
||||||
@@ -40,10 +40,9 @@ custom_minimum_size = Vector2(80, 30)
|
|||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
size_flags_vertical = 0
|
size_flags_vertical = 0
|
||||||
item_count = 9
|
|
||||||
selected = 7
|
selected = 7
|
||||||
|
item_count = 9
|
||||||
popup/item_0/text = "Left_UL"
|
popup/item_0/text = "Left_UL"
|
||||||
popup/item_0/id = 0
|
|
||||||
popup/item_1/text = "Left_BL"
|
popup/item_1/text = "Left_BL"
|
||||||
popup/item_1/id = 1
|
popup/item_1/id = 1
|
||||||
popup/item_2/text = "Left_UR"
|
popup/item_2/text = "Left_UR"
|
||||||
@@ -65,7 +64,7 @@ popup/item_8/id = 8
|
|||||||
custom_minimum_size = Vector2(80, 10)
|
custom_minimum_size = Vector2(80, 10)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
min_value = 56.0
|
min_value = 66.0
|
||||||
max_value = 230.0
|
max_value = 230.0
|
||||||
value = 83.0
|
value = 83.0
|
||||||
|
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
uid://dft2g3p2pjw12
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
uid://byi465tyumga0
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
uid://y7pm2ndr5k2m
|
|
||||||
@@ -1,553 +0,0 @@
|
|||||||
[gd_scene load_steps=7 format=3 uid="uid://nud6dwjcnj5v"]
|
|
||||||
|
|
||||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_ysabf"]
|
|
||||||
bg_color = Color(0.211765, 0.239216, 0.290196, 1)
|
|
||||||
|
|
||||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_lcvna"]
|
|
||||||
bg_color = Color(0.168627, 0.211765, 0.266667, 1)
|
|
||||||
border_width_left = 3
|
|
||||||
border_width_top = 3
|
|
||||||
border_width_right = 3
|
|
||||||
border_width_bottom = 3
|
|
||||||
border_color = Color(0.270588, 0.435294, 0.580392, 1)
|
|
||||||
corner_radius_top_left = 5
|
|
||||||
corner_radius_top_right = 5
|
|
||||||
corner_radius_bottom_right = 5
|
|
||||||
corner_radius_bottom_left = 5
|
|
||||||
|
|
||||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_cb0xf"]
|
|
||||||
bg_color = Color(0.137255, 0.137255, 0.137255, 1)
|
|
||||||
draw_center = false
|
|
||||||
border_width_left = 3
|
|
||||||
border_width_top = 3
|
|
||||||
border_width_right = 3
|
|
||||||
border_width_bottom = 3
|
|
||||||
border_color = Color(0.784314, 0.784314, 0.784314, 1)
|
|
||||||
corner_radius_top_left = 5
|
|
||||||
corner_radius_top_right = 5
|
|
||||||
corner_radius_bottom_right = 5
|
|
||||||
corner_radius_bottom_left = 5
|
|
||||||
|
|
||||||
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_7qdas"]
|
|
||||||
|
|
||||||
[sub_resource type="ButtonGroup" id="ButtonGroup_wnxik"]
|
|
||||||
|
|
||||||
[sub_resource type="ButtonGroup" id="ButtonGroup_bs6ki"]
|
|
||||||
|
|
||||||
[node name="Window" type="Window"]
|
|
||||||
title = "Terrain3D Channel Packer"
|
|
||||||
initial_position = 1
|
|
||||||
size = Vector2i(680, 835)
|
|
||||||
unresizable = true
|
|
||||||
always_on_top = true
|
|
||||||
|
|
||||||
[node name="Panel" type="Panel" parent="."]
|
|
||||||
anchors_preset = 15
|
|
||||||
anchor_right = 1.0
|
|
||||||
anchor_bottom = 1.0
|
|
||||||
grow_horizontal = 2
|
|
||||||
grow_vertical = 2
|
|
||||||
theme_override_styles/panel = SubResource("StyleBoxFlat_ysabf")
|
|
||||||
|
|
||||||
[node name="MarginContainer" type="MarginContainer" parent="Panel"]
|
|
||||||
layout_mode = 1
|
|
||||||
anchors_preset = 15
|
|
||||||
anchor_right = 1.0
|
|
||||||
anchor_bottom = 1.0
|
|
||||||
offset_left = 5.0
|
|
||||||
offset_top = 5.0
|
|
||||||
offset_right = -5.0
|
|
||||||
offset_bottom = 5.0
|
|
||||||
grow_horizontal = 2
|
|
||||||
grow_vertical = 2
|
|
||||||
theme_override_constants/margin_left = 5
|
|
||||||
theme_override_constants/margin_top = 5
|
|
||||||
theme_override_constants/margin_right = 5
|
|
||||||
theme_override_constants/margin_bottom = 5
|
|
||||||
|
|
||||||
[node name="VBoxContainer" type="VBoxContainer" parent="Panel/MarginContainer"]
|
|
||||||
layout_mode = 2
|
|
||||||
theme_override_constants/separation = 10
|
|
||||||
|
|
||||||
[node name="AlbedoHeightPanel" type="Panel" parent="Panel/MarginContainer/VBoxContainer"]
|
|
||||||
custom_minimum_size = Vector2(0, 290)
|
|
||||||
layout_mode = 2
|
|
||||||
mouse_filter = 1
|
|
||||||
theme_override_styles/panel = SubResource("StyleBoxFlat_lcvna")
|
|
||||||
|
|
||||||
[node name="MarginContainer" type="MarginContainer" parent="Panel/MarginContainer/VBoxContainer/AlbedoHeightPanel"]
|
|
||||||
layout_mode = 1
|
|
||||||
anchors_preset = 15
|
|
||||||
anchor_right = 1.0
|
|
||||||
anchor_bottom = 1.0
|
|
||||||
grow_horizontal = 2
|
|
||||||
grow_vertical = 2
|
|
||||||
theme_override_constants/margin_left = 10
|
|
||||||
theme_override_constants/margin_top = 10
|
|
||||||
theme_override_constants/margin_right = 10
|
|
||||||
theme_override_constants/margin_bottom = 10
|
|
||||||
|
|
||||||
[node name="HBoxContainer" type="HBoxContainer" parent="Panel/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer"]
|
|
||||||
layout_mode = 2
|
|
||||||
|
|
||||||
[node name="AlbedoVBox" type="VBoxContainer" parent="Panel/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer"]
|
|
||||||
layout_mode = 2
|
|
||||||
size_flags_horizontal = 3
|
|
||||||
|
|
||||||
[node name="AlbedoLabel" type="Label" parent="Panel/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/AlbedoVBox"]
|
|
||||||
layout_mode = 2
|
|
||||||
text = "Albedo texture"
|
|
||||||
|
|
||||||
[node name="AlbedoHBox" type="HBoxContainer" parent="Panel/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/AlbedoVBox"]
|
|
||||||
layout_mode = 2
|
|
||||||
|
|
||||||
[node name="LineEdit" type="LineEdit" parent="Panel/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/AlbedoVBox/AlbedoHBox"]
|
|
||||||
layout_mode = 2
|
|
||||||
size_flags_horizontal = 3
|
|
||||||
|
|
||||||
[node name="PickButton" type="Button" parent="Panel/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/AlbedoVBox/AlbedoHBox"]
|
|
||||||
layout_mode = 2
|
|
||||||
|
|
||||||
[node name="ClearButton" type="Button" parent="Panel/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/AlbedoVBox/AlbedoHBox"]
|
|
||||||
layout_mode = 2
|
|
||||||
|
|
||||||
[node name="MarginContainer" type="MarginContainer" parent="Panel/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/AlbedoVBox"]
|
|
||||||
layout_mode = 2
|
|
||||||
size_flags_vertical = 4
|
|
||||||
theme_override_constants/margin_top = 10
|
|
||||||
|
|
||||||
[node name="Panel" type="Panel" parent="Panel/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/AlbedoVBox/MarginContainer"]
|
|
||||||
custom_minimum_size = Vector2(110, 110)
|
|
||||||
layout_mode = 2
|
|
||||||
size_flags_horizontal = 4
|
|
||||||
size_flags_vertical = 4
|
|
||||||
theme_override_styles/panel = SubResource("StyleBoxFlat_cb0xf")
|
|
||||||
|
|
||||||
[node name="TextureRect" type="TextureRect" parent="Panel/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/AlbedoVBox/MarginContainer/Panel"]
|
|
||||||
layout_mode = 1
|
|
||||||
anchors_preset = 8
|
|
||||||
anchor_left = 0.5
|
|
||||||
anchor_top = 0.5
|
|
||||||
anchor_right = 0.5
|
|
||||||
anchor_bottom = 0.5
|
|
||||||
offset_left = -50.0
|
|
||||||
offset_top = -50.0
|
|
||||||
offset_right = 50.0
|
|
||||||
offset_bottom = 50.0
|
|
||||||
grow_horizontal = 2
|
|
||||||
grow_vertical = 2
|
|
||||||
expand_mode = 1
|
|
||||||
|
|
||||||
[node name="TextureButton" type="Button" parent="Panel/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/AlbedoVBox/MarginContainer/Panel"]
|
|
||||||
layout_mode = 1
|
|
||||||
anchors_preset = 15
|
|
||||||
anchor_right = 1.0
|
|
||||||
anchor_bottom = 1.0
|
|
||||||
grow_horizontal = 2
|
|
||||||
grow_vertical = 2
|
|
||||||
theme_override_styles/normal = SubResource("StyleBoxEmpty_7qdas")
|
|
||||||
|
|
||||||
[node name="AlbedoWHHBox" type="HBoxContainer" parent="Panel/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/AlbedoVBox"]
|
|
||||||
layout_mode = 2
|
|
||||||
alignment = 1
|
|
||||||
|
|
||||||
[node name="AlbedoW" type="Label" parent="Panel/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/AlbedoVBox/AlbedoWHHBox"]
|
|
||||||
layout_mode = 2
|
|
||||||
horizontal_alignment = 1
|
|
||||||
|
|
||||||
[node name="AlbedoH" type="Label" parent="Panel/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/AlbedoVBox/AlbedoWHHBox"]
|
|
||||||
layout_mode = 2
|
|
||||||
horizontal_alignment = 1
|
|
||||||
|
|
||||||
[node name="HBoxContainer2" type="HBoxContainer" parent="Panel/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/AlbedoVBox"]
|
|
||||||
layout_mode = 2
|
|
||||||
alignment = 1
|
|
||||||
|
|
||||||
[node name="LuminanceAsHeightButton" type="Button" parent="Panel/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/AlbedoVBox/HBoxContainer2"]
|
|
||||||
layout_mode = 2
|
|
||||||
text = " Generate Height from Luminance"
|
|
||||||
icon_alignment = 2
|
|
||||||
|
|
||||||
[node name="HeightVBox" type="VBoxContainer" parent="Panel/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer"]
|
|
||||||
layout_mode = 2
|
|
||||||
size_flags_horizontal = 3
|
|
||||||
|
|
||||||
[node name="HeightLabel" type="Label" parent="Panel/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/HeightVBox"]
|
|
||||||
layout_mode = 2
|
|
||||||
text = "Height texture"
|
|
||||||
|
|
||||||
[node name="HeightHBox" type="HBoxContainer" parent="Panel/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/HeightVBox"]
|
|
||||||
layout_mode = 2
|
|
||||||
|
|
||||||
[node name="LineEdit" type="LineEdit" parent="Panel/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/HeightVBox/HeightHBox"]
|
|
||||||
layout_mode = 2
|
|
||||||
size_flags_horizontal = 3
|
|
||||||
|
|
||||||
[node name="PickButton" type="Button" parent="Panel/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/HeightVBox/HeightHBox"]
|
|
||||||
layout_mode = 2
|
|
||||||
|
|
||||||
[node name="ClearButton" type="Button" parent="Panel/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/HeightVBox/HeightHBox"]
|
|
||||||
layout_mode = 2
|
|
||||||
|
|
||||||
[node name="MarginContainer" type="MarginContainer" parent="Panel/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/HeightVBox"]
|
|
||||||
layout_mode = 2
|
|
||||||
size_flags_vertical = 4
|
|
||||||
theme_override_constants/margin_top = 10
|
|
||||||
|
|
||||||
[node name="Panel" type="Panel" parent="Panel/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/HeightVBox/MarginContainer"]
|
|
||||||
custom_minimum_size = Vector2(110, 110)
|
|
||||||
layout_mode = 2
|
|
||||||
size_flags_horizontal = 4
|
|
||||||
size_flags_vertical = 4
|
|
||||||
theme_override_styles/panel = SubResource("StyleBoxFlat_cb0xf")
|
|
||||||
|
|
||||||
[node name="TextureRect" type="TextureRect" parent="Panel/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/HeightVBox/MarginContainer/Panel"]
|
|
||||||
layout_mode = 1
|
|
||||||
anchors_preset = 8
|
|
||||||
anchor_left = 0.5
|
|
||||||
anchor_top = 0.5
|
|
||||||
anchor_right = 0.5
|
|
||||||
anchor_bottom = 0.5
|
|
||||||
offset_left = -50.0
|
|
||||||
offset_top = -50.0
|
|
||||||
offset_right = 50.0
|
|
||||||
offset_bottom = 50.0
|
|
||||||
grow_horizontal = 2
|
|
||||||
grow_vertical = 2
|
|
||||||
expand_mode = 1
|
|
||||||
|
|
||||||
[node name="TextureButton" type="Button" parent="Panel/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/HeightVBox/MarginContainer/Panel"]
|
|
||||||
layout_mode = 1
|
|
||||||
anchors_preset = 15
|
|
||||||
anchor_right = 1.0
|
|
||||||
anchor_bottom = 1.0
|
|
||||||
grow_horizontal = 2
|
|
||||||
grow_vertical = 2
|
|
||||||
theme_override_styles/normal = SubResource("StyleBoxEmpty_7qdas")
|
|
||||||
|
|
||||||
[node name="HeightWHHBox" type="HBoxContainer" parent="Panel/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/HeightVBox"]
|
|
||||||
layout_mode = 2
|
|
||||||
alignment = 1
|
|
||||||
|
|
||||||
[node name="HeightW" type="Label" parent="Panel/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/HeightVBox/HeightWHHBox"]
|
|
||||||
layout_mode = 2
|
|
||||||
horizontal_alignment = 1
|
|
||||||
|
|
||||||
[node name="HeightH" type="Label" parent="Panel/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/HeightVBox/HeightWHHBox"]
|
|
||||||
layout_mode = 2
|
|
||||||
horizontal_alignment = 1
|
|
||||||
|
|
||||||
[node name="HBoxContainer2" type="HBoxContainer" parent="Panel/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/HeightVBox"]
|
|
||||||
layout_mode = 2
|
|
||||||
alignment = 1
|
|
||||||
|
|
||||||
[node name="ConvertDepthToHeight" type="CheckBox" parent="Panel/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/HeightVBox/HBoxContainer2"]
|
|
||||||
layout_mode = 2
|
|
||||||
text = " Convert Depth to Height"
|
|
||||||
icon_alignment = 2
|
|
||||||
|
|
||||||
[node name="HBoxContainer" type="HBoxContainer" parent="Panel/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/HeightVBox"]
|
|
||||||
layout_mode = 2
|
|
||||||
alignment = 1
|
|
||||||
|
|
||||||
[node name="HeightChannelLabel" type="Label" parent="Panel/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/HeightVBox/HBoxContainer"]
|
|
||||||
layout_mode = 2
|
|
||||||
text = " Source Channel: "
|
|
||||||
horizontal_alignment = 2
|
|
||||||
|
|
||||||
[node name="HeightChannelR" type="Button" parent="Panel/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/HeightVBox/HBoxContainer"]
|
|
||||||
layout_mode = 2
|
|
||||||
toggle_mode = true
|
|
||||||
button_pressed = true
|
|
||||||
button_group = SubResource("ButtonGroup_wnxik")
|
|
||||||
text = "R"
|
|
||||||
|
|
||||||
[node name="HeightChannelB" type="Button" parent="Panel/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/HeightVBox/HBoxContainer"]
|
|
||||||
layout_mode = 2
|
|
||||||
toggle_mode = true
|
|
||||||
button_group = SubResource("ButtonGroup_wnxik")
|
|
||||||
text = "G"
|
|
||||||
|
|
||||||
[node name="HeightChannelG" type="Button" parent="Panel/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/HeightVBox/HBoxContainer"]
|
|
||||||
layout_mode = 2
|
|
||||||
toggle_mode = true
|
|
||||||
button_group = SubResource("ButtonGroup_wnxik")
|
|
||||||
text = "B"
|
|
||||||
|
|
||||||
[node name="HeightChannelA" type="Button" parent="Panel/MarginContainer/VBoxContainer/AlbedoHeightPanel/MarginContainer/HBoxContainer/HeightVBox/HBoxContainer"]
|
|
||||||
layout_mode = 2
|
|
||||||
toggle_mode = true
|
|
||||||
button_group = SubResource("ButtonGroup_wnxik")
|
|
||||||
text = "A"
|
|
||||||
|
|
||||||
[node name="NormalRoughnessPanel" type="Panel" parent="Panel/MarginContainer/VBoxContainer"]
|
|
||||||
custom_minimum_size = Vector2(0, 290)
|
|
||||||
layout_mode = 2
|
|
||||||
theme_override_styles/panel = SubResource("StyleBoxFlat_lcvna")
|
|
||||||
|
|
||||||
[node name="MarginContainer" type="MarginContainer" parent="Panel/MarginContainer/VBoxContainer/NormalRoughnessPanel"]
|
|
||||||
layout_mode = 1
|
|
||||||
anchors_preset = 15
|
|
||||||
anchor_right = 1.0
|
|
||||||
anchor_bottom = 1.0
|
|
||||||
grow_horizontal = 2
|
|
||||||
grow_vertical = 2
|
|
||||||
theme_override_constants/margin_left = 10
|
|
||||||
theme_override_constants/margin_top = 10
|
|
||||||
theme_override_constants/margin_right = 10
|
|
||||||
theme_override_constants/margin_bottom = 10
|
|
||||||
|
|
||||||
[node name="HBoxContainer" type="HBoxContainer" parent="Panel/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer"]
|
|
||||||
layout_mode = 2
|
|
||||||
|
|
||||||
[node name="NormalVBox" type="VBoxContainer" parent="Panel/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer"]
|
|
||||||
layout_mode = 2
|
|
||||||
size_flags_horizontal = 3
|
|
||||||
|
|
||||||
[node name="NormalLabel" type="Label" parent="Panel/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/NormalVBox"]
|
|
||||||
layout_mode = 2
|
|
||||||
text = "Normal texture"
|
|
||||||
|
|
||||||
[node name="NormalHBox" type="HBoxContainer" parent="Panel/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/NormalVBox"]
|
|
||||||
layout_mode = 2
|
|
||||||
|
|
||||||
[node name="LineEdit" type="LineEdit" parent="Panel/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/NormalVBox/NormalHBox"]
|
|
||||||
layout_mode = 2
|
|
||||||
size_flags_horizontal = 3
|
|
||||||
|
|
||||||
[node name="PickButton" type="Button" parent="Panel/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/NormalVBox/NormalHBox"]
|
|
||||||
layout_mode = 2
|
|
||||||
|
|
||||||
[node name="ClearButton" type="Button" parent="Panel/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/NormalVBox/NormalHBox"]
|
|
||||||
layout_mode = 2
|
|
||||||
|
|
||||||
[node name="MarginContainer" type="MarginContainer" parent="Panel/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/NormalVBox"]
|
|
||||||
layout_mode = 2
|
|
||||||
size_flags_vertical = 4
|
|
||||||
theme_override_constants/margin_top = 10
|
|
||||||
|
|
||||||
[node name="Panel" type="Panel" parent="Panel/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/NormalVBox/MarginContainer"]
|
|
||||||
custom_minimum_size = Vector2(110, 110)
|
|
||||||
layout_mode = 2
|
|
||||||
size_flags_horizontal = 4
|
|
||||||
size_flags_vertical = 4
|
|
||||||
theme_override_styles/panel = SubResource("StyleBoxFlat_cb0xf")
|
|
||||||
|
|
||||||
[node name="TextureRect" type="TextureRect" parent="Panel/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/NormalVBox/MarginContainer/Panel"]
|
|
||||||
layout_mode = 1
|
|
||||||
anchors_preset = 8
|
|
||||||
anchor_left = 0.5
|
|
||||||
anchor_top = 0.5
|
|
||||||
anchor_right = 0.5
|
|
||||||
anchor_bottom = 0.5
|
|
||||||
offset_left = -50.0
|
|
||||||
offset_top = -50.0
|
|
||||||
offset_right = 50.0
|
|
||||||
offset_bottom = 50.0
|
|
||||||
grow_horizontal = 2
|
|
||||||
grow_vertical = 2
|
|
||||||
expand_mode = 1
|
|
||||||
|
|
||||||
[node name="TextureButton" type="Button" parent="Panel/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/NormalVBox/MarginContainer/Panel"]
|
|
||||||
layout_mode = 1
|
|
||||||
anchors_preset = 15
|
|
||||||
anchor_right = 1.0
|
|
||||||
anchor_bottom = 1.0
|
|
||||||
grow_horizontal = 2
|
|
||||||
grow_vertical = 2
|
|
||||||
theme_override_styles/normal = SubResource("StyleBoxEmpty_7qdas")
|
|
||||||
|
|
||||||
[node name="NormalWHHBox" type="HBoxContainer" parent="Panel/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/NormalVBox"]
|
|
||||||
layout_mode = 2
|
|
||||||
alignment = 1
|
|
||||||
|
|
||||||
[node name="NormalW" type="Label" parent="Panel/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/NormalVBox/NormalWHHBox"]
|
|
||||||
layout_mode = 2
|
|
||||||
horizontal_alignment = 1
|
|
||||||
|
|
||||||
[node name="NormalH" type="Label" parent="Panel/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/NormalVBox/NormalWHHBox"]
|
|
||||||
layout_mode = 2
|
|
||||||
horizontal_alignment = 1
|
|
||||||
|
|
||||||
[node name="HBoxContainer" type="HBoxContainer" parent="Panel/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/NormalVBox"]
|
|
||||||
layout_mode = 2
|
|
||||||
alignment = 1
|
|
||||||
|
|
||||||
[node name="InvertGreenChannelCheckBox" type="CheckBox" parent="Panel/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/NormalVBox/HBoxContainer"]
|
|
||||||
layout_mode = 2
|
|
||||||
text = " Convert DirectX to OpenGL"
|
|
||||||
|
|
||||||
[node name="HBoxContainer2" type="HBoxContainer" parent="Panel/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/NormalVBox"]
|
|
||||||
layout_mode = 2
|
|
||||||
alignment = 1
|
|
||||||
|
|
||||||
[node name="AlignNormalsCheckBox" type="CheckBox" parent="Panel/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/NormalVBox/HBoxContainer2"]
|
|
||||||
layout_mode = 2
|
|
||||||
text = " Orthoganolise Normals"
|
|
||||||
|
|
||||||
[node name="RoughnessVBox" type="VBoxContainer" parent="Panel/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer"]
|
|
||||||
layout_mode = 2
|
|
||||||
size_flags_horizontal = 3
|
|
||||||
|
|
||||||
[node name="RoughnessLabel" type="Label" parent="Panel/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/RoughnessVBox"]
|
|
||||||
layout_mode = 2
|
|
||||||
text = "Roughness texture"
|
|
||||||
|
|
||||||
[node name="RoughnessHBox" type="HBoxContainer" parent="Panel/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/RoughnessVBox"]
|
|
||||||
layout_mode = 2
|
|
||||||
|
|
||||||
[node name="LineEdit" type="LineEdit" parent="Panel/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/RoughnessVBox/RoughnessHBox"]
|
|
||||||
layout_mode = 2
|
|
||||||
size_flags_horizontal = 3
|
|
||||||
|
|
||||||
[node name="PickButton" type="Button" parent="Panel/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/RoughnessVBox/RoughnessHBox"]
|
|
||||||
layout_mode = 2
|
|
||||||
|
|
||||||
[node name="ClearButton" type="Button" parent="Panel/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/RoughnessVBox/RoughnessHBox"]
|
|
||||||
layout_mode = 2
|
|
||||||
|
|
||||||
[node name="MarginContainer" type="MarginContainer" parent="Panel/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/RoughnessVBox"]
|
|
||||||
layout_mode = 2
|
|
||||||
size_flags_vertical = 4
|
|
||||||
theme_override_constants/margin_top = 10
|
|
||||||
|
|
||||||
[node name="Panel" type="Panel" parent="Panel/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/RoughnessVBox/MarginContainer"]
|
|
||||||
custom_minimum_size = Vector2(110, 110)
|
|
||||||
layout_mode = 2
|
|
||||||
size_flags_horizontal = 4
|
|
||||||
size_flags_vertical = 4
|
|
||||||
theme_override_styles/panel = SubResource("StyleBoxFlat_cb0xf")
|
|
||||||
|
|
||||||
[node name="TextureRect" type="TextureRect" parent="Panel/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/RoughnessVBox/MarginContainer/Panel"]
|
|
||||||
layout_mode = 1
|
|
||||||
anchors_preset = 8
|
|
||||||
anchor_left = 0.5
|
|
||||||
anchor_top = 0.5
|
|
||||||
anchor_right = 0.5
|
|
||||||
anchor_bottom = 0.5
|
|
||||||
offset_left = -50.0
|
|
||||||
offset_top = -50.0
|
|
||||||
offset_right = 50.0
|
|
||||||
offset_bottom = 50.0
|
|
||||||
grow_horizontal = 2
|
|
||||||
grow_vertical = 2
|
|
||||||
expand_mode = 1
|
|
||||||
|
|
||||||
[node name="TextureButton" type="Button" parent="Panel/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/RoughnessVBox/MarginContainer/Panel"]
|
|
||||||
layout_mode = 1
|
|
||||||
anchors_preset = 15
|
|
||||||
anchor_right = 1.0
|
|
||||||
anchor_bottom = 1.0
|
|
||||||
grow_horizontal = 2
|
|
||||||
grow_vertical = 2
|
|
||||||
theme_override_styles/normal = SubResource("StyleBoxEmpty_7qdas")
|
|
||||||
|
|
||||||
[node name="RoughnessWHHBox" type="HBoxContainer" parent="Panel/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/RoughnessVBox"]
|
|
||||||
layout_mode = 2
|
|
||||||
alignment = 1
|
|
||||||
|
|
||||||
[node name="RoughnessW" type="Label" parent="Panel/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/RoughnessVBox/RoughnessWHHBox"]
|
|
||||||
layout_mode = 2
|
|
||||||
horizontal_alignment = 1
|
|
||||||
|
|
||||||
[node name="RoughnessH" type="Label" parent="Panel/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/RoughnessVBox/RoughnessWHHBox"]
|
|
||||||
layout_mode = 2
|
|
||||||
horizontal_alignment = 1
|
|
||||||
|
|
||||||
[node name="HBoxContainer2" type="HBoxContainer" parent="Panel/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/RoughnessVBox"]
|
|
||||||
layout_mode = 2
|
|
||||||
alignment = 1
|
|
||||||
|
|
||||||
[node name="InvertSmoothCheckBox" type="CheckBox" parent="Panel/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/RoughnessVBox/HBoxContainer2"]
|
|
||||||
layout_mode = 2
|
|
||||||
text = " Convert Smoothness to Roughness"
|
|
||||||
|
|
||||||
[node name="HBoxContainer" type="HBoxContainer" parent="Panel/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/RoughnessVBox"]
|
|
||||||
layout_mode = 2
|
|
||||||
alignment = 1
|
|
||||||
|
|
||||||
[node name="RoughnessChannelLabel" type="Label" parent="Panel/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/RoughnessVBox/HBoxContainer"]
|
|
||||||
layout_mode = 2
|
|
||||||
text = " Source Channel: "
|
|
||||||
horizontal_alignment = 2
|
|
||||||
|
|
||||||
[node name="RoughnessChannelR" type="Button" parent="Panel/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/RoughnessVBox/HBoxContainer"]
|
|
||||||
layout_mode = 2
|
|
||||||
toggle_mode = true
|
|
||||||
button_pressed = true
|
|
||||||
button_group = SubResource("ButtonGroup_bs6ki")
|
|
||||||
text = "R"
|
|
||||||
|
|
||||||
[node name="RoughnessChannelG" type="Button" parent="Panel/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/RoughnessVBox/HBoxContainer"]
|
|
||||||
layout_mode = 2
|
|
||||||
toggle_mode = true
|
|
||||||
button_group = SubResource("ButtonGroup_bs6ki")
|
|
||||||
text = "G"
|
|
||||||
|
|
||||||
[node name="RoughnessChannelB" type="Button" parent="Panel/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/RoughnessVBox/HBoxContainer"]
|
|
||||||
layout_mode = 2
|
|
||||||
toggle_mode = true
|
|
||||||
button_group = SubResource("ButtonGroup_bs6ki")
|
|
||||||
text = "B"
|
|
||||||
|
|
||||||
[node name="RoughnessChannelA" type="Button" parent="Panel/MarginContainer/VBoxContainer/NormalRoughnessPanel/MarginContainer/HBoxContainer/RoughnessVBox/HBoxContainer"]
|
|
||||||
layout_mode = 2
|
|
||||||
toggle_mode = true
|
|
||||||
button_group = SubResource("ButtonGroup_bs6ki")
|
|
||||||
text = "A"
|
|
||||||
|
|
||||||
[node name="GeneralOptionsLabel" type="Label" parent="Panel/MarginContainer/VBoxContainer"]
|
|
||||||
layout_mode = 2
|
|
||||||
text = "General Options"
|
|
||||||
horizontal_alignment = 1
|
|
||||||
vertical_alignment = 1
|
|
||||||
|
|
||||||
[node name="GeneralOptionsHBox" type="HBoxContainer" parent="Panel/MarginContainer/VBoxContainer"]
|
|
||||||
custom_minimum_size = Vector2(0, 35)
|
|
||||||
layout_mode = 2
|
|
||||||
alignment = 1
|
|
||||||
|
|
||||||
[node name="ResizeToggle" type="CheckBox" parent="Panel/MarginContainer/VBoxContainer/GeneralOptionsHBox"]
|
|
||||||
layout_mode = 2
|
|
||||||
text = " Resize Packed Image"
|
|
||||||
|
|
||||||
[node name="ResizeOptionButton" type="SpinBox" parent="Panel/MarginContainer/VBoxContainer/GeneralOptionsHBox"]
|
|
||||||
visible = false
|
|
||||||
layout_mode = 2
|
|
||||||
tooltip_text = "A value of 0 disables resizing."
|
|
||||||
min_value = 128.0
|
|
||||||
max_value = 4096.0
|
|
||||||
step = 128.0
|
|
||||||
value = 1024.0
|
|
||||||
|
|
||||||
[node name="VSeparator" type="VSeparator" parent="Panel/MarginContainer/VBoxContainer/GeneralOptionsHBox"]
|
|
||||||
layout_mode = 2
|
|
||||||
|
|
||||||
[node name="GenerateMipmapsCheckBox" type="CheckBox" parent="Panel/MarginContainer/VBoxContainer/GeneralOptionsHBox"]
|
|
||||||
layout_mode = 2
|
|
||||||
button_pressed = true
|
|
||||||
text = "Generate Mipmaps"
|
|
||||||
|
|
||||||
[node name="HighQualityCheckBox" type="CheckBox" parent="Panel/MarginContainer/VBoxContainer/GeneralOptionsHBox"]
|
|
||||||
layout_mode = 2
|
|
||||||
text = "Import High Quality"
|
|
||||||
|
|
||||||
[node name="PackButton" type="Button" parent="Panel/MarginContainer/VBoxContainer"]
|
|
||||||
layout_mode = 2
|
|
||||||
text = "Pack textures as..."
|
|
||||||
|
|
||||||
[node name="StatusLabel" type="Label" parent="Panel/MarginContainer/VBoxContainer"]
|
|
||||||
custom_minimum_size = Vector2(0, 60)
|
|
||||||
layout_mode = 2
|
|
||||||
horizontal_alignment = 1
|
|
||||||
autowrap_mode = 3
|
|
||||||
|
|
||||||
[node name="HBoxContainer" type="HBoxContainer" parent="Panel/MarginContainer/VBoxContainer"]
|
|
||||||
layout_mode = 2
|
|
||||||
alignment = 1
|
|
||||||
|
|
||||||
[node name="CloseButton" type="Button" parent="Panel/MarginContainer/VBoxContainer/HBoxContainer"]
|
|
||||||
layout_mode = 2
|
|
||||||
text = "Close"
|
|
||||||
|
|
||||||
[connection signal="toggled" from="Panel/MarginContainer/VBoxContainer/GeneralOptionsHBox/ResizeToggle" to="Panel/MarginContainer/VBoxContainer/GeneralOptionsHBox/ResizeOptionButton" method="set_visible"]
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
uid://r0eex2idvm56
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
uid://dt80i8xsj8tun
|
|
||||||
@@ -1,3 +1,6 @@
|
|||||||
|
# Copyright © 2025 Cory Petkovsek, Roope Palmroos, and Contributors.
|
||||||
|
# DoubleSlider for Terrain3D
|
||||||
|
# Should work for other UIs
|
||||||
@tool
|
@tool
|
||||||
class_name DoubleSlider
|
class_name DoubleSlider
|
||||||
extends Control
|
extends Control
|
||||||
@@ -10,9 +13,23 @@ var min_value: float = 0.0
|
|||||||
var max_value: float = 100.0
|
var max_value: float = 100.0
|
||||||
var step: float = 1.0
|
var step: float = 1.0
|
||||||
var range := Vector2(0, 100)
|
var range := Vector2(0, 100)
|
||||||
|
var display_scale: float = 1.
|
||||||
|
var position_x: float = 0.
|
||||||
|
var minimum_x: float = 60.
|
||||||
|
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
|
# Setup Display Scale
|
||||||
|
# 0 auto, 1 75%, 2 100%, 3 125%, 4 150%, 5 175%, 6 200%, 7 custom
|
||||||
|
var es: EditorSettings = EditorInterface.get_editor_settings()
|
||||||
|
var ds: int = es.get_setting("interface/editor/display_scale")
|
||||||
|
if ds == 0:
|
||||||
|
ds = 2
|
||||||
|
elif ds == 7:
|
||||||
|
display_scale = es.get_setting("interface/editor/custom_display_scale")
|
||||||
|
else:
|
||||||
|
display_scale = float(ds + 2) * .25
|
||||||
|
|
||||||
update_label()
|
update_label()
|
||||||
|
|
||||||
|
|
||||||
@@ -68,11 +85,21 @@ func get_value() -> Vector2:
|
|||||||
func update_label() -> void:
|
func update_label() -> void:
|
||||||
if label:
|
if label:
|
||||||
label.set_text(str(range.x) + suffix + "/" + str(range.y) + suffix)
|
label.set_text(str(range.x) + suffix + "/" + str(range.y) + suffix)
|
||||||
|
if position_x == 0:
|
||||||
|
position_x = label.position.x
|
||||||
|
else:
|
||||||
|
label.position.x = position_x + 5 * display_scale
|
||||||
|
label.custom_minimum_size.x = minimum_x + 5 * display_scale
|
||||||
|
|
||||||
|
|
||||||
|
func _get_handle() -> int:
|
||||||
|
return 1
|
||||||
|
|
||||||
|
|
||||||
func _gui_input(p_event: InputEvent) -> void:
|
func _gui_input(p_event: InputEvent) -> void:
|
||||||
if p_event is InputEventMouseButton:
|
if p_event is InputEventMouseButton:
|
||||||
if p_event.get_button_index() == MOUSE_BUTTON_LEFT:
|
var button: int = p_event.get_button_index()
|
||||||
|
if button in [ MOUSE_BUTTON_LEFT, MOUSE_BUTTON_WHEEL_UP, MOUSE_BUTTON_WHEEL_DOWN ]:
|
||||||
if p_event.is_pressed():
|
if p_event.is_pressed():
|
||||||
var mid_point = (range.x + range.y) / 2.0
|
var mid_point = (range.x + range.y) / 2.0
|
||||||
var xpos: float = p_event.get_position().x * 2.0
|
var xpos: float = p_event.get_position().x * 2.0
|
||||||
@@ -80,7 +107,13 @@ func _gui_input(p_event: InputEvent) -> void:
|
|||||||
grabbed_handle = 1
|
grabbed_handle = 1
|
||||||
else:
|
else:
|
||||||
grabbed_handle = -1
|
grabbed_handle = -1
|
||||||
|
match button:
|
||||||
|
MOUSE_BUTTON_LEFT:
|
||||||
set_slider(p_event.get_position().x)
|
set_slider(p_event.get_position().x)
|
||||||
|
MOUSE_BUTTON_WHEEL_DOWN:
|
||||||
|
set_slider(-1., true)
|
||||||
|
MOUSE_BUTTON_WHEEL_UP:
|
||||||
|
set_slider(1., true)
|
||||||
else:
|
else:
|
||||||
grabbed_handle = 0
|
grabbed_handle = 0
|
||||||
|
|
||||||
@@ -89,12 +122,18 @@ func _gui_input(p_event: InputEvent) -> void:
|
|||||||
set_slider(p_event.get_position().x)
|
set_slider(p_event.get_position().x)
|
||||||
|
|
||||||
|
|
||||||
func set_slider(p_xpos: float) -> void:
|
func set_slider(p_xpos: float, p_relative: bool = false) -> void:
|
||||||
if grabbed_handle == 0:
|
if grabbed_handle == 0:
|
||||||
return
|
return
|
||||||
var xpos_step: float = clamp(snappedf((p_xpos / size.x) * max_value, step), min_value, max_value)
|
var xpos_step: float = clamp(snappedf((p_xpos / size.x) * max_value, step), min_value, max_value)
|
||||||
if(grabbed_handle < 0):
|
if(grabbed_handle < 0):
|
||||||
|
if p_relative:
|
||||||
|
range.x += p_xpos
|
||||||
|
else:
|
||||||
range.x = xpos_step
|
range.x = xpos_step
|
||||||
|
else:
|
||||||
|
if p_relative:
|
||||||
|
range.y += p_xpos
|
||||||
else:
|
else:
|
||||||
range.y = xpos_step
|
range.y = xpos_step
|
||||||
set_value(range)
|
set_value(range)
|
||||||
@@ -111,14 +150,15 @@ func _notification(p_what: int) -> void:
|
|||||||
# Draw foreground bar
|
# Draw foreground bar
|
||||||
var handle: Texture2D = get_theme_icon("grabber", "HSlider")
|
var handle: Texture2D = get_theme_icon("grabber", "HSlider")
|
||||||
var area: StyleBox = get_theme_stylebox("grabber_area", "HSlider")
|
var area: StyleBox = get_theme_stylebox("grabber_area", "HSlider")
|
||||||
var h: float = size.y / 2 - handle.get_size().y / 2
|
|
||||||
var startx: float = (range.x / max_value) * size.x
|
var startx: float = (range.x / max_value) * size.x
|
||||||
var endx: float = (range.y / max_value) * size.x #- startx
|
var endx: float = (range.y / max_value) * size.x
|
||||||
draw_style_box(area, Rect2(Vector2(startx, mid_y), Vector2(endx - startx, bg_height)))
|
draw_style_box(area, Rect2(Vector2(startx, mid_y), Vector2(endx - startx, bg_height)))
|
||||||
|
|
||||||
# Draw handles, slightly in so they don't get on the outside edges
|
# Draw handles, slightly in so they don't get on the outside edges
|
||||||
var handle_pos: Vector2
|
var handle_pos: Vector2
|
||||||
handle_pos.x = clamp(startx - handle.get_size().x/2, -5, size.x)
|
handle_pos.x = clamp(startx - handle.get_size().x/2, -10, size.x)
|
||||||
handle_pos.y = clamp(endx - handle.get_size().x/2, 0, size.x - 10)
|
handle_pos.y = clamp(endx - handle.get_size().x/2, 0, size.x - 10)
|
||||||
draw_texture(handle, Vector2(handle_pos.x, -mid_y))
|
draw_texture(handle, Vector2(handle_pos.x, -mid_y - 10 * (display_scale - 1.)))
|
||||||
draw_texture(handle, Vector2(handle_pos.y, -mid_y))
|
draw_texture(handle, Vector2(handle_pos.y, -mid_y - 10 * (display_scale - 1.)))
|
||||||
|
|
||||||
|
update_label()
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
uid://cd6164a8pp48f
|
uid://stro0p1oawfb
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
|
# Copyright © 2025 Cory Petkovsek, Roope Palmroos, and Contributors.
|
||||||
|
# Editor Plugin for Terrain3D
|
||||||
@tool
|
@tool
|
||||||
extends EditorPlugin
|
extends EditorPlugin
|
||||||
#class_name Terrain3DEditorPlugin Cannot be named until Godot #75388
|
|
||||||
|
|
||||||
|
|
||||||
# Includes
|
# Includes
|
||||||
@@ -13,6 +14,7 @@ var modifier_alt: bool
|
|||||||
var modifier_shift: bool
|
var modifier_shift: bool
|
||||||
var _last_modifiers: int = 0
|
var _last_modifiers: int = 0
|
||||||
var _input_mode: int = 0 # -1: camera move, 0: none, 1: operating
|
var _input_mode: int = 0 # -1: camera move, 0: none, 1: operating
|
||||||
|
var _use_meta: bool = false
|
||||||
|
|
||||||
var terrain: Terrain3D
|
var terrain: Terrain3D
|
||||||
var _last_terrain: Terrain3D
|
var _last_terrain: Terrain3D
|
||||||
@@ -29,6 +31,9 @@ var godot_editor_window: Window # The Godot Editor window
|
|||||||
|
|
||||||
|
|
||||||
func _init() -> void:
|
func _init() -> void:
|
||||||
|
if OS.get_name() == "macOS":
|
||||||
|
_use_meta = true
|
||||||
|
|
||||||
# Get the Godot Editor window. Structure is root:Window/EditorNode/Base Control
|
# Get the Godot Editor window. Structure is root:Window/EditorNode/Base Control
|
||||||
godot_editor_window = EditorInterface.get_base_control().get_parent().get_parent()
|
godot_editor_window = EditorInterface.get_base_control().get_parent().get_parent()
|
||||||
godot_editor_window.focus_entered.connect(_on_godot_focus_entered)
|
godot_editor_window.focus_entered.connect(_on_godot_focus_entered)
|
||||||
@@ -113,10 +118,6 @@ func _edit(p_object: Object) -> void:
|
|||||||
ui.set_visible(true)
|
ui.set_visible(true)
|
||||||
terrain.set_meta("_edit_lock_", true)
|
terrain.set_meta("_edit_lock_", true)
|
||||||
|
|
||||||
# Deprecated 0.9.3 - Remove 1.0
|
|
||||||
if terrain.storage:
|
|
||||||
ui.terrain_menu.directory_setup.directory_setup_popup()
|
|
||||||
|
|
||||||
# Get alerted when a new asset list is loaded
|
# Get alerted when a new asset list is loaded
|
||||||
if not terrain.assets_changed.is_connected(asset_dock.update_assets):
|
if not terrain.assets_changed.is_connected(asset_dock.update_assets):
|
||||||
terrain.assets_changed.connect(asset_dock.update_assets)
|
terrain.assets_changed.connect(asset_dock.update_assets)
|
||||||
@@ -155,10 +156,12 @@ func _forward_3d_gui_input(p_viewport_camera: Camera3D, p_event: InputEvent) ->
|
|||||||
return AFTER_GUI_INPUT_PASS
|
return AFTER_GUI_INPUT_PASS
|
||||||
|
|
||||||
_read_input(p_event)
|
_read_input(p_event)
|
||||||
|
ui.update_decal()
|
||||||
|
|
||||||
## Handle mouse movement
|
|
||||||
if p_event is InputEventMouseMotion:
|
|
||||||
## Setup active camera & viewport
|
## Setup active camera & viewport
|
||||||
|
# Always update this for all inputs, as the mouse position can move without
|
||||||
|
# necessarily being a InputEventMouseMotion object. get_intersection() also
|
||||||
|
# returns the last frame position, and should be updated more frequently.
|
||||||
|
|
||||||
# Snap terrain to current camera
|
# Snap terrain to current camera
|
||||||
terrain.set_camera(p_viewport_camera)
|
terrain.set_camera(p_viewport_camera)
|
||||||
@@ -169,9 +172,9 @@ func _forward_3d_gui_input(p_viewport_camera: Camera3D, p_event: InputEvent) ->
|
|||||||
var full_resolution: bool = false if editor_vpc.stretch_shrink == 2 else true
|
var full_resolution: bool = false if editor_vpc.stretch_shrink == 2 else true
|
||||||
|
|
||||||
## Get mouse location on terrain
|
## Get mouse location on terrain
|
||||||
|
|
||||||
# Project 2D mouse position to 3D position and direction
|
# Project 2D mouse position to 3D position and direction
|
||||||
var mouse_pos: Vector2 = p_event.position if full_resolution else p_event.position/2
|
var vp_mouse_pos: Vector2 = editor_vpc.get_local_mouse_position()
|
||||||
|
var mouse_pos: Vector2 = vp_mouse_pos if full_resolution else vp_mouse_pos / 2
|
||||||
var camera_pos: Vector3 = p_viewport_camera.project_ray_origin(mouse_pos)
|
var camera_pos: Vector3 = p_viewport_camera.project_ray_origin(mouse_pos)
|
||||||
var camera_dir: Vector3 = p_viewport_camera.project_ray_normal(mouse_pos)
|
var camera_dir: Vector3 = p_viewport_camera.project_ray_normal(mouse_pos)
|
||||||
|
|
||||||
@@ -180,13 +183,14 @@ func _forward_3d_gui_input(p_viewport_camera: Camera3D, p_event: InputEvent) ->
|
|||||||
var t = -Vector3(0, 1, 0).dot(camera_pos) / Vector3(0, 1, 0).dot(camera_dir)
|
var t = -Vector3(0, 1, 0).dot(camera_pos) / Vector3(0, 1, 0).dot(camera_dir)
|
||||||
mouse_global_position = (camera_pos + t * camera_dir)
|
mouse_global_position = (camera_pos + t * camera_dir)
|
||||||
else:
|
else:
|
||||||
# Else look for intersection with terrain
|
#Else look for intersection with terrain
|
||||||
var intersection_point: Vector3 = terrain.get_intersection(camera_pos, camera_dir)
|
var intersection_point: Vector3 = terrain.get_intersection(camera_pos, camera_dir, true)
|
||||||
if intersection_point.z > 3.4e38 or is_nan(intersection_point.y): # max double or nan
|
if intersection_point.z > 3.4e38 or is_nan(intersection_point.y): # max double or nan
|
||||||
return AFTER_GUI_INPUT_STOP
|
return AFTER_GUI_INPUT_PASS
|
||||||
mouse_global_position = intersection_point
|
mouse_global_position = intersection_point
|
||||||
|
|
||||||
ui.update_decal()
|
## Handle mouse movement
|
||||||
|
if p_event is InputEventMouseMotion:
|
||||||
|
|
||||||
if _input_mode != -1: # Not cam rotation
|
if _input_mode != -1: # Not cam rotation
|
||||||
## Update region highlight
|
## Update region highlight
|
||||||
@@ -205,8 +209,6 @@ func _forward_3d_gui_input(p_viewport_camera: Camera3D, p_event: InputEvent) ->
|
|||||||
|
|
||||||
return AFTER_GUI_INPUT_PASS
|
return AFTER_GUI_INPUT_PASS
|
||||||
|
|
||||||
ui.update_decal()
|
|
||||||
|
|
||||||
if p_event is InputEventMouseButton and _input_mode > 0:
|
if p_event is InputEventMouseButton and _input_mode > 0:
|
||||||
if p_event.is_pressed():
|
if p_event.is_pressed():
|
||||||
# If picking
|
# If picking
|
||||||
@@ -215,6 +217,11 @@ func _forward_3d_gui_input(p_viewport_camera: Camera3D, p_event: InputEvent) ->
|
|||||||
if not ui.operation_builder or not ui.operation_builder.is_ready():
|
if not ui.operation_builder or not ui.operation_builder.is_ready():
|
||||||
return AFTER_GUI_INPUT_STOP
|
return AFTER_GUI_INPUT_STOP
|
||||||
|
|
||||||
|
if modifier_ctrl and editor.get_tool() == Terrain3DEditor.HEIGHT:
|
||||||
|
var height: float = terrain.data.get_height(mouse_global_position)
|
||||||
|
ui.brush_data["height"] = height
|
||||||
|
ui.tool_settings.set_setting("height", height)
|
||||||
|
|
||||||
# If adjusting regions
|
# If adjusting regions
|
||||||
if editor.get_tool() == Terrain3DEditor.REGION:
|
if editor.get_tool() == Terrain3DEditor.REGION:
|
||||||
# Skip regions that already exist or don't
|
# Skip regions that already exist or don't
|
||||||
@@ -273,7 +280,7 @@ func _read_input(p_event: InputEvent = null) -> void:
|
|||||||
|
|
||||||
## Determine modifiers pressed
|
## Determine modifiers pressed
|
||||||
modifier_shift = Input.is_key_pressed(KEY_SHIFT)
|
modifier_shift = Input.is_key_pressed(KEY_SHIFT)
|
||||||
modifier_ctrl = Input.is_key_pressed(KEY_CTRL)
|
modifier_ctrl = Input.is_key_pressed(KEY_META) if _use_meta else Input.is_key_pressed(KEY_CTRL)
|
||||||
# Keybind enum: Alt,Space,Meta,Capslock
|
# Keybind enum: Alt,Space,Meta,Capslock
|
||||||
var alt_key: int
|
var alt_key: int
|
||||||
match get_setting("terrain3d/config/alt_key_bind", 0):
|
match get_setting("terrain3d/config/alt_key_bind", 0):
|
||||||
@@ -370,29 +377,6 @@ func setup_editor_settings() -> void:
|
|||||||
}
|
}
|
||||||
editor_settings.add_property_info(property_info)
|
editor_settings.add_property_info(property_info)
|
||||||
|
|
||||||
_cleanup_old_settings()
|
|
||||||
|
|
||||||
|
|
||||||
# Remove or rename old settings
|
|
||||||
func _cleanup_old_settings() -> void:
|
|
||||||
# Rename deprecated settings - Remove in 1.0
|
|
||||||
var value: Variant
|
|
||||||
var rename_arr := [ "terrain3d/config/dock_slot", "terrain3d/config/dock_tile_size",
|
|
||||||
"terrain3d/config/dock_floating", "terrain3d/config/dock_always_on_top",
|
|
||||||
"terrain3d/config/dock_window_size", "terrain3d/config/dock_window_position", ]
|
|
||||||
for es: String in rename_arr:
|
|
||||||
if editor_settings.has_setting(es):
|
|
||||||
value = editor_settings.get_setting(es)
|
|
||||||
editor_settings.erase(es)
|
|
||||||
editor_settings.set_setting(es.replace("/config/dock_", "/dock/"), value)
|
|
||||||
|
|
||||||
# Special handling
|
|
||||||
var es: String = "terrain3d/tool_settings/slope"
|
|
||||||
if editor_settings.has_setting(es):
|
|
||||||
value = editor_settings.get_setting(es)
|
|
||||||
if typeof(value) == TYPE_FLOAT:
|
|
||||||
editor_settings.erase(es)
|
|
||||||
|
|
||||||
|
|
||||||
func set_setting(p_str: String, p_value: Variant) -> void:
|
func set_setting(p_str: String, p_value: Variant) -> void:
|
||||||
editor_settings.set_setting(p_str, p_value)
|
editor_settings.set_setting(p_str, p_value)
|
||||||
1
addons/terrain_3d/src/editor_plugin.gd.uid
Normal file
1
addons/terrain_3d/src/editor_plugin.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://bsgxo1qywjdf3
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
# Copyright © 2025 Cory Petkovsek, Roope Palmroos, and Contributors.
|
||||||
|
# Gradient Operation Builder for Terrain3D
|
||||||
extends "res://addons/terrain_3d/src/operation_builder.gd"
|
extends "res://addons/terrain_3d/src/operation_builder.gd"
|
||||||
|
|
||||||
|
|
||||||
@@ -52,4 +54,3 @@ func apply_operation(p_editor: Terrain3DEditor, p_global_position: Vector3, p_ca
|
|||||||
p_editor.stop_operation()
|
p_editor.stop_operation()
|
||||||
|
|
||||||
_get_point_picker().clear()
|
_get_point_picker().clear()
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
uid://b1qt62vc3vnx7
|
uid://def7sych6dp8b
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
# Copyright © 2025 Cory Petkovsek, Roope Palmroos, and Contributors.
|
||||||
|
# Multipicker for Terrain3D
|
||||||
extends HBoxContainer
|
extends HBoxContainer
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
uid://dqwxbfvjra3p3
|
uid://dvdtoa32h6xdn
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
# Copyright © 2025 Cory Petkovsek, Roope Palmroos, and Contributors.
|
||||||
|
# Operation Builder for Terrain3D
|
||||||
extends RefCounted
|
extends RefCounted
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
uid://3rphgkvw2bi1
|
uid://bu5cm0eh052rm
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
# Copyright © 2025 Cory Petkovsek, Roope Palmroos, and Contributors.
|
||||||
|
# Editor Region Gizmos for Terrain3D
|
||||||
extends EditorNode3DGizmo
|
extends EditorNode3DGizmo
|
||||||
|
|
||||||
var material: StandardMaterial3D
|
var material: StandardMaterial3D
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
uid://ddnxpafte8kb7
|
uid://bh6qwe1ok4cx3
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
uid://cbtr86lrjls7a
|
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
# Copyright © 2025 Cory Petkovsek, Roope Palmroos, and Contributors.
|
||||||
|
# Tool settings bar for Terrain3D
|
||||||
extends PanelContainer
|
extends PanelContainer
|
||||||
|
|
||||||
signal picking(type, callback)
|
signal picking(type, callback)
|
||||||
@@ -39,6 +41,7 @@ const NO_SAVE: int = 0x20 # Don't save this in EditorSettings
|
|||||||
var plugin: EditorPlugin # Actually Terrain3DEditorPlugin, but Godot still has CRC errors
|
var plugin: EditorPlugin # Actually Terrain3DEditorPlugin, but Godot still has CRC errors
|
||||||
var brush_preview_material: ShaderMaterial
|
var brush_preview_material: ShaderMaterial
|
||||||
var select_brush_button: Button
|
var select_brush_button: Button
|
||||||
|
var selected_brush_imgs: Array
|
||||||
var main_list: HFlowContainer
|
var main_list: HFlowContainer
|
||||||
var advanced_list: VBoxContainer
|
var advanced_list: VBoxContainer
|
||||||
var height_list: VBoxContainer
|
var height_list: VBoxContainer
|
||||||
@@ -86,6 +89,9 @@ func _ready() -> void:
|
|||||||
add_setting({ "name":"enable_texture", "label":"Texture", "type":SettingType.CHECKBOX,
|
add_setting({ "name":"enable_texture", "label":"Texture", "type":SettingType.CHECKBOX,
|
||||||
"list":main_list, "default":true, "flags":ADD_SEPARATOR })
|
"list":main_list, "default":true, "flags":ADD_SEPARATOR })
|
||||||
|
|
||||||
|
add_setting({ "name":"texture_filter", "label":"Texture Filter", "type":SettingType.CHECKBOX,
|
||||||
|
"list":main_list, "default":false, "flags":ADD_SEPARATOR })
|
||||||
|
|
||||||
add_setting({ "name":"margin", "type":SettingType.SLIDER, "list":main_list, "default":0,
|
add_setting({ "name":"margin", "type":SettingType.SLIDER, "list":main_list, "default":0,
|
||||||
"unit":"", "range":Vector3(-50, 50, 1), "flags":ALLOW_OUT_OF_BOUNDS })
|
"unit":"", "range":Vector3(-50, 50, 1), "flags":ALLOW_OUT_OF_BOUNDS })
|
||||||
|
|
||||||
@@ -171,6 +177,8 @@ func _ready() -> void:
|
|||||||
"unit":"γ", "range":Vector3(0.1, 2.0, 0.01) })
|
"unit":"γ", "range":Vector3(0.1, 2.0, 0.01) })
|
||||||
add_setting({ "name":"jitter", "type":SettingType.SLIDER, "list":advanced_list, "default":50,
|
add_setting({ "name":"jitter", "type":SettingType.SLIDER, "list":advanced_list, "default":50,
|
||||||
"unit":"%", "range":Vector3(0, 100, 1) })
|
"unit":"%", "range":Vector3(0, 100, 1) })
|
||||||
|
add_setting({ "name":"crosshair_threshold", "type":SettingType.SLIDER, "list":advanced_list, "default":16.,
|
||||||
|
"unit":"m", "range":Vector3(0, 200, 1) })
|
||||||
|
|
||||||
|
|
||||||
func create_submenu(p_parent: Control, p_button_name: String, p_layout: Layout, p_hover_pop: bool = true) -> Container:
|
func create_submenu(p_parent: Control, p_button_name: String, p_layout: Layout, p_hover_pop: bool = true) -> Container:
|
||||||
@@ -267,29 +275,33 @@ func add_brushes(p_parent: Control) -> void:
|
|||||||
while file_name != "":
|
while file_name != "":
|
||||||
if !dir.current_is_dir() and file_name.ends_with(".exr"):
|
if !dir.current_is_dir() and file_name.ends_with(".exr"):
|
||||||
var img: Image = Image.load_from_file(BRUSH_PATH + "/" + file_name)
|
var img: Image = Image.load_from_file(BRUSH_PATH + "/" + file_name)
|
||||||
img = Terrain3DUtil.black_to_alpha(img)
|
var thumbimg: Image = img.duplicate()
|
||||||
if img.get_width() < 1024 and img.get_height() < 1024:
|
img.convert(Image.FORMAT_RF)
|
||||||
img.resize(1024, 1024, Image.INTERPOLATE_CUBIC)
|
|
||||||
var tex: ImageTexture = ImageTexture.create_from_image(img)
|
|
||||||
|
|
||||||
var btn: Button = Button.new()
|
if thumbimg.get_width() != 100 and thumbimg.get_height() != 100:
|
||||||
btn.set_custom_minimum_size(Vector2.ONE * 100)
|
thumbimg.resize(100, 100, Image.INTERPOLATE_CUBIC)
|
||||||
btn.set_button_icon(tex)
|
thumbimg = Terrain3DUtil.black_to_alpha(thumbimg)
|
||||||
btn.set_meta("image", img)
|
thumbimg.convert(Image.FORMAT_LA8)
|
||||||
btn.set_expand_icon(true)
|
var thumbtex: ImageTexture = ImageTexture.create_from_image(thumbimg)
|
||||||
btn.set_material(_get_brush_preview_material())
|
|
||||||
btn.set_toggle_mode(true)
|
var brush_btn: Button = Button.new()
|
||||||
btn.set_button_group(brush_button_group)
|
brush_btn.set_custom_minimum_size(Vector2.ONE * 100)
|
||||||
btn.mouse_entered.connect(_on_brush_hover.bind(true, btn))
|
brush_btn.set_button_icon(thumbtex)
|
||||||
btn.mouse_exited.connect(_on_brush_hover.bind(false, btn))
|
brush_btn.set_meta("image", img)
|
||||||
brush_list.add_child(btn, true)
|
brush_btn.set_expand_icon(true)
|
||||||
|
brush_btn.set_material(_get_brush_preview_material())
|
||||||
|
brush_btn.set_toggle_mode(true)
|
||||||
|
brush_btn.set_button_group(brush_button_group)
|
||||||
|
brush_btn.mouse_entered.connect(_on_brush_hover.bind(true, brush_btn))
|
||||||
|
brush_btn.mouse_exited.connect(_on_brush_hover.bind(false, brush_btn))
|
||||||
|
brush_list.add_child(brush_btn, true)
|
||||||
if file_name == DEFAULT_BRUSH:
|
if file_name == DEFAULT_BRUSH:
|
||||||
default_brush_btn = btn
|
default_brush_btn = brush_btn
|
||||||
|
|
||||||
var lbl: Label = Label.new()
|
var lbl: Label = Label.new()
|
||||||
btn.name = file_name.get_basename().to_pascal_case()
|
brush_btn.name = file_name.get_basename().to_pascal_case()
|
||||||
btn.add_child(lbl, true)
|
brush_btn.add_child(lbl, true)
|
||||||
lbl.text = btn.name
|
lbl.text = brush_btn.name
|
||||||
lbl.visible = false
|
lbl.visible = false
|
||||||
lbl.position.y = 70
|
lbl.position.y = 70
|
||||||
lbl.add_theme_color_override("font_shadow_color", Color.BLACK)
|
lbl.add_theme_color_override("font_shadow_color", Color.BLACK)
|
||||||
@@ -304,6 +316,7 @@ func add_brushes(p_parent: Control) -> void:
|
|||||||
if not default_brush_btn:
|
if not default_brush_btn:
|
||||||
default_brush_btn = brush_button_group.get_buttons()[0]
|
default_brush_btn = brush_button_group.get_buttons()[0]
|
||||||
default_brush_btn.set_pressed(true)
|
default_brush_btn.set_pressed(true)
|
||||||
|
_generate_brush_texture(default_brush_btn)
|
||||||
|
|
||||||
settings["brush"] = brush_button_group
|
settings["brush"] = brush_button_group
|
||||||
|
|
||||||
@@ -473,7 +486,7 @@ func add_setting(p_args: Dictionary) -> void:
|
|||||||
else: # DOUBLE_SLIDER
|
else: # DOUBLE_SLIDER
|
||||||
var label := Label.new()
|
var label := Label.new()
|
||||||
label.set_custom_minimum_size(Vector2(60, 0))
|
label.set_custom_minimum_size(Vector2(60, 0))
|
||||||
label.set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT)
|
label.set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER)
|
||||||
slider = DoubleSlider.new()
|
slider = DoubleSlider.new()
|
||||||
slider.label = label
|
slider.label = label
|
||||||
slider.suffix = p_suffix
|
slider.suffix = p_suffix
|
||||||
@@ -560,9 +573,7 @@ func get_setting(p_setting: String) -> Variant:
|
|||||||
elif object is DoubleSlider:
|
elif object is DoubleSlider:
|
||||||
value = object.get_value()
|
value = object.get_value()
|
||||||
elif object is ButtonGroup: # "brush"
|
elif object is ButtonGroup: # "brush"
|
||||||
var img: Image = object.get_pressed_button().get_meta("image")
|
value = selected_brush_imgs
|
||||||
var tex: Texture2D = object.get_pressed_button().get_button_icon()
|
|
||||||
value = [ img, tex ]
|
|
||||||
elif object is CheckBox:
|
elif object is CheckBox:
|
||||||
value = object.is_pressed()
|
value = object.is_pressed()
|
||||||
elif object is ColorPickerButton:
|
elif object is ColorPickerButton:
|
||||||
@@ -610,20 +621,31 @@ func show_settings(p_settings: PackedStringArray) -> void:
|
|||||||
select_brush_button.show()
|
select_brush_button.show()
|
||||||
|
|
||||||
|
|
||||||
func _on_setting_changed(p_data: Variant = null) -> void:
|
func _on_setting_changed(p_object: Variant = null) -> void:
|
||||||
# If a button was clicked on a submenu
|
# If a brush was selected
|
||||||
if p_data is Button and p_data.get_parent().get_parent() is PopupPanel:
|
if p_object is Button and p_object.get_parent().name == "BrushList":
|
||||||
if p_data.get_parent().name == "BrushList":
|
_generate_brush_texture(p_object)
|
||||||
# Optionally Set selected brush texture in main brush button
|
# Optionally Set selected brush texture in main brush button
|
||||||
p_data.get_parent().get_parent().get_parent().set_button_icon(p_data.get_button_icon())
|
if select_brush_button:
|
||||||
|
select_brush_button.set_button_icon(p_object.get_button_icon())
|
||||||
# Hide popup
|
# Hide popup
|
||||||
p_data.get_parent().get_parent().set_visible(false)
|
p_object.get_parent().get_parent().set_visible(false)
|
||||||
# Hide label
|
# Hide label
|
||||||
if p_data.get_child_count() > 0:
|
if p_object.get_child_count() > 0:
|
||||||
p_data.get_child(0).visible = false
|
p_object.get_child(0).visible = false
|
||||||
emit_signal("setting_changed")
|
emit_signal("setting_changed")
|
||||||
|
|
||||||
|
|
||||||
|
func _generate_brush_texture(p_btn: Button) -> void:
|
||||||
|
if p_btn is Button:
|
||||||
|
var img: Image = p_btn.get_meta("image")
|
||||||
|
if img.get_width() < 1024 and img.get_height() < 1024:
|
||||||
|
img = img.duplicate()
|
||||||
|
img.resize(1024, 1024, Image.INTERPOLATE_CUBIC)
|
||||||
|
var tex: ImageTexture = ImageTexture.create_from_image(img)
|
||||||
|
selected_brush_imgs = [ img, tex ]
|
||||||
|
|
||||||
|
|
||||||
func _on_drawable_toggled(p_button_pressed: bool) -> void:
|
func _on_drawable_toggled(p_button_pressed: bool) -> void:
|
||||||
if not p_button_pressed:
|
if not p_button_pressed:
|
||||||
settings["gradient_points"].clear()
|
settings["gradient_points"].clear()
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
uid://b0hs7rbtc8jfy
|
uid://ciskaaennrffu
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
# Copyright © 2025 Cory Petkovsek, Roope Palmroos, and Contributors.
|
||||||
|
# Toolbar for Terrain3D
|
||||||
extends VFlowContainer
|
extends VFlowContainer
|
||||||
|
|
||||||
signal tool_changed(p_tool: Terrain3DEditor.Tool, p_operation: Terrain3DEditor.Operation)
|
signal tool_changed(p_tool: Terrain3DEditor.Tool, p_operation: Terrain3DEditor.Operation)
|
||||||
@@ -26,8 +28,8 @@ func _init() -> void:
|
|||||||
set_custom_minimum_size(Vector2(20, 0))
|
set_custom_minimum_size(Vector2(20, 0))
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
add_tool_group.connect("pressed", _on_tool_selected)
|
add_tool_group.pressed.connect(_on_tool_selected)
|
||||||
sub_tool_group.connect("pressed", _on_tool_selected)
|
sub_tool_group.pressed.connect(_on_tool_selected)
|
||||||
|
|
||||||
add_tool_button({ "tool":Terrain3DEditor.REGION,
|
add_tool_button({ "tool":Terrain3DEditor.REGION,
|
||||||
"add_text":"Add Region", "add_op":Terrain3DEditor.ADD, "add_icon":ICON_REGION_ADD,
|
"add_text":"Add Region", "add_op":Terrain3DEditor.ADD, "add_icon":ICON_REGION_ADD,
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
uid://bthdcp8t4awsk
|
uid://b1j37u6utjbom
|
||||||
|
|||||||
@@ -1,16 +1,17 @@
|
|||||||
|
# Copyright © 2025 Cory Petkovsek, Roope Palmroos, and Contributors.
|
||||||
|
# UI for Terrain3D
|
||||||
extends Node
|
extends Node
|
||||||
#class_name Terrain3DUI Cannot be named until Godot #75388
|
|
||||||
|
|
||||||
|
|
||||||
# Includes
|
# Includes
|
||||||
|
const TerrainMenu: Script = preload("res://addons/terrain_3d/menu/terrain_menu.gd")
|
||||||
const Toolbar: Script = preload("res://addons/terrain_3d/src/toolbar.gd")
|
const Toolbar: Script = preload("res://addons/terrain_3d/src/toolbar.gd")
|
||||||
const ToolSettings: Script = preload("res://addons/terrain_3d/src/tool_settings.gd")
|
const ToolSettings: Script = preload("res://addons/terrain_3d/src/tool_settings.gd")
|
||||||
const TerrainMenu: Script = preload("res://addons/terrain_3d/src/terrain_menu.gd")
|
|
||||||
const OperationBuilder: Script = preload("res://addons/terrain_3d/src/operation_builder.gd")
|
const OperationBuilder: Script = preload("res://addons/terrain_3d/src/operation_builder.gd")
|
||||||
const GradientOperationBuilder: Script = preload("res://addons/terrain_3d/src/gradient_operation_builder.gd")
|
const GradientOperationBuilder: Script = preload("res://addons/terrain_3d/src/gradient_operation_builder.gd")
|
||||||
const COLOR_RAISE := Color.WHITE
|
const COLOR_RAISE := Color.WHITE
|
||||||
const COLOR_LOWER := Color(.02, .02, .02)
|
const COLOR_LOWER := Color.BLACK
|
||||||
const COLOR_SMOOTH := Color(0.5, 0, .1)
|
const COLOR_SMOOTH := Color(0.5, 0, .2)
|
||||||
const COLOR_LIFT := Color.ORANGE
|
const COLOR_LIFT := Color.ORANGE
|
||||||
const COLOR_FLATTEN := Color.BLUE_VIOLET
|
const COLOR_FLATTEN := Color.BLUE_VIOLET
|
||||||
const COLOR_HEIGHT := Color(0., 0.32, .4)
|
const COLOR_HEIGHT := Color(0., 0.32, .4)
|
||||||
@@ -20,7 +21,7 @@ const COLOR_SPRAY := Color.PALE_GREEN
|
|||||||
const COLOR_ROUGHNESS := Color.ROYAL_BLUE
|
const COLOR_ROUGHNESS := Color.ROYAL_BLUE
|
||||||
const COLOR_AUTOSHADER := Color.DODGER_BLUE
|
const COLOR_AUTOSHADER := Color.DODGER_BLUE
|
||||||
const COLOR_HOLES := Color.BLACK
|
const COLOR_HOLES := Color.BLACK
|
||||||
const COLOR_NAVIGATION := Color(.15, .0, .255)
|
const COLOR_NAVIGATION := Color(.28, .0, .25)
|
||||||
const COLOR_INSTANCER := Color.CRIMSON
|
const COLOR_INSTANCER := Color.CRIMSON
|
||||||
const COLOR_PICK_COLOR := Color.WHITE
|
const COLOR_PICK_COLOR := Color.WHITE
|
||||||
const COLOR_PICK_HEIGHT := Color.DARK_RED
|
const COLOR_PICK_HEIGHT := Color.DARK_RED
|
||||||
@@ -31,8 +32,13 @@ const OP_POSITIVE_ONLY: int = 0x01
|
|||||||
const OP_NEGATIVE_ONLY: int = 0x02
|
const OP_NEGATIVE_ONLY: int = 0x02
|
||||||
|
|
||||||
const RING1: String = "res://addons/terrain_3d/brushes/ring1.exr"
|
const RING1: String = "res://addons/terrain_3d/brushes/ring1.exr"
|
||||||
@onready var ring_texture := ImageTexture.create_from_image(Terrain3DUtil.black_to_alpha(Image.load_from_file(RING1)))
|
var ring_texture : ImageTexture
|
||||||
|
@onready var region_texture := ImageTexture.new() :
|
||||||
|
set(value):
|
||||||
|
var image: Image = Image.create_empty(1, 1, false, Image.FORMAT_R8)
|
||||||
|
image.fill(Color.WHITE)
|
||||||
|
value.create_from_image(image)
|
||||||
|
region_texture = value
|
||||||
var plugin: EditorPlugin # Actually Terrain3DEditorPlugin, but Godot still has CRC errors
|
var plugin: EditorPlugin # Actually Terrain3DEditorPlugin, but Godot still has CRC errors
|
||||||
var toolbar: Toolbar
|
var toolbar: Toolbar
|
||||||
var tool_settings: ToolSettings
|
var tool_settings: ToolSettings
|
||||||
@@ -41,31 +47,42 @@ var setting_has_changed: bool = false
|
|||||||
var visible: bool = false
|
var visible: bool = false
|
||||||
var picking: int = Terrain3DEditor.TOOL_MAX
|
var picking: int = Terrain3DEditor.TOOL_MAX
|
||||||
var picking_callback: Callable
|
var picking_callback: Callable
|
||||||
var decal: Decal
|
|
||||||
var decal_timer: Timer
|
|
||||||
var gradient_decals: Array[Decal]
|
|
||||||
var brush_data: Dictionary
|
var brush_data: Dictionary
|
||||||
var operation_builder: OperationBuilder
|
var operation_builder: OperationBuilder
|
||||||
var last_tool: Terrain3DEditor.Tool
|
var last_tool: Terrain3DEditor.Tool
|
||||||
var last_operation: Terrain3DEditor.Operation
|
var last_operation: Terrain3DEditor.Operation
|
||||||
var last_rmb_time: int = 0 # Set in editor.gd
|
var last_rmb_time: int = 0 # Set in editor.gd
|
||||||
|
|
||||||
# Compatibility decals, indices; 0 = main brush, 1 = slope point A, 2 = slope point B
|
# Editor decals, indices; 0 = main brush, 1 = slope point A, 2 = slope point B
|
||||||
var editor_decal_position: Array[Vector2]
|
var mat_rid: RID
|
||||||
var editor_decal_rotation: Array[float]
|
var editor_decal_position: Array[Vector2] = [Vector2(), Vector2(), Vector2()]
|
||||||
var editor_decal_size: Array[float]
|
var editor_decal_rotation: Array[float] = [float(), float(), float()]
|
||||||
var editor_decal_color: Array[Color]
|
var editor_decal_size: Array[float] = [float(), float(), float()]
|
||||||
var editor_decal_visible: Array[bool]
|
var editor_decal_color: Array[Color] = [Color(), Color(), Color()]
|
||||||
|
var editor_decal_visible: Array[bool] = [bool(), bool(), bool()]
|
||||||
|
var editor_brush_texture_rid: RID = RID()
|
||||||
|
var editor_decal_timer: Timer
|
||||||
|
var editor_decal_fade: float :
|
||||||
|
set(value):
|
||||||
|
editor_decal_fade = value
|
||||||
|
if editor_decal_color.size() > 0:
|
||||||
|
editor_decal_color[0].a = value
|
||||||
|
if is_shader_valid():
|
||||||
|
RenderingServer.material_set_param(mat_rid, "_editor_decal_color", editor_decal_color)
|
||||||
|
if value < 0.001:
|
||||||
|
var r_map: PackedInt32Array = plugin.terrain.data.get_region_map()
|
||||||
|
RenderingServer.material_set_param(mat_rid, "_region_map", r_map)
|
||||||
|
var editor_ring_texture_rid: RID
|
||||||
|
|
||||||
|
|
||||||
func _enter_tree() -> void:
|
func _enter_tree() -> void:
|
||||||
toolbar = Toolbar.new()
|
toolbar = Toolbar.new()
|
||||||
toolbar.hide()
|
toolbar.hide()
|
||||||
toolbar.connect("tool_changed", _on_tool_changed)
|
toolbar.tool_changed.connect(_on_tool_changed)
|
||||||
|
|
||||||
tool_settings = ToolSettings.new()
|
tool_settings = ToolSettings.new()
|
||||||
tool_settings.connect("setting_changed", _on_setting_changed)
|
tool_settings.setting_changed.connect(_on_setting_changed)
|
||||||
tool_settings.connect("picking", _on_picking)
|
tool_settings.picking.connect(_on_picking)
|
||||||
tool_settings.plugin = plugin
|
tool_settings.plugin = plugin
|
||||||
tool_settings.hide()
|
tool_settings.hide()
|
||||||
|
|
||||||
@@ -79,15 +96,19 @@ func _enter_tree() -> void:
|
|||||||
|
|
||||||
_on_tool_changed(Terrain3DEditor.REGION, Terrain3DEditor.ADD)
|
_on_tool_changed(Terrain3DEditor.REGION, Terrain3DEditor.ADD)
|
||||||
|
|
||||||
decal = Decal.new()
|
editor_decal_timer = Timer.new()
|
||||||
add_child(decal)
|
editor_decal_timer.wait_time = .5
|
||||||
decal_timer = Timer.new()
|
editor_decal_timer.one_shot = true
|
||||||
decal_timer.wait_time = .5
|
editor_decal_timer.timeout.connect(func():
|
||||||
decal_timer.one_shot = true
|
get_tree().create_tween().tween_property(self, "editor_decal_fade", 0.0, 0.15))
|
||||||
decal_timer.timeout.connect(Callable(func(node):
|
add_child(editor_decal_timer)
|
||||||
if node:
|
|
||||||
get_tree().create_tween().tween_property(node, "albedo_mix", 0.0, 0.15)).bind(decal))
|
|
||||||
add_child(decal_timer)
|
func _ready() -> void:
|
||||||
|
var img: Image = Image.load_from_file(RING1)
|
||||||
|
img.convert(Image.FORMAT_R8)
|
||||||
|
ring_texture = ImageTexture.create_from_image(img)
|
||||||
|
editor_ring_texture_rid = ring_texture.get_rid()
|
||||||
|
|
||||||
|
|
||||||
func _exit_tree() -> void:
|
func _exit_tree() -> void:
|
||||||
@@ -96,11 +117,7 @@ func _exit_tree() -> void:
|
|||||||
toolbar.queue_free()
|
toolbar.queue_free()
|
||||||
tool_settings.queue_free()
|
tool_settings.queue_free()
|
||||||
terrain_menu.queue_free()
|
terrain_menu.queue_free()
|
||||||
decal.queue_free()
|
editor_decal_timer.queue_free()
|
||||||
decal_timer.queue_free()
|
|
||||||
for gradient_decal in gradient_decals:
|
|
||||||
gradient_decal.queue_free()
|
|
||||||
gradient_decals.clear()
|
|
||||||
|
|
||||||
|
|
||||||
func set_visible(p_visible: bool, p_menu_only: bool = false) -> void:
|
func set_visible(p_visible: bool, p_menu_only: bool = false) -> void:
|
||||||
@@ -117,7 +134,7 @@ func set_visible(p_visible: bool, p_menu_only: bool = false) -> void:
|
|||||||
|
|
||||||
if(plugin.editor):
|
if(plugin.editor):
|
||||||
if(p_visible):
|
if(p_visible):
|
||||||
await get_tree().create_timer(.01).timeout # Won't work, otherwise.
|
await get_tree().create_timer(.01).timeout # Won't work, otherwise
|
||||||
_on_tool_changed(last_tool, last_operation)
|
_on_tool_changed(last_tool, last_operation)
|
||||||
else:
|
else:
|
||||||
plugin.editor.set_tool(Terrain3DEditor.TOOL_MAX)
|
plugin.editor.set_tool(Terrain3DEditor.TOOL_MAX)
|
||||||
@@ -185,7 +202,7 @@ func _on_tool_changed(p_tool: Terrain3DEditor.Tool, p_operation: Terrain3DEditor
|
|||||||
to_show.push_back("color")
|
to_show.push_back("color")
|
||||||
to_show.push_back("color_picker")
|
to_show.push_back("color_picker")
|
||||||
to_show.push_back("slope")
|
to_show.push_back("slope")
|
||||||
to_show.push_back("enable_texture")
|
to_show.push_back("texture_filter")
|
||||||
to_show.push_back("margin")
|
to_show.push_back("margin")
|
||||||
to_show.push_back("remove")
|
to_show.push_back("remove")
|
||||||
|
|
||||||
@@ -196,7 +213,7 @@ func _on_tool_changed(p_tool: Terrain3DEditor.Tool, p_operation: Terrain3DEditor
|
|||||||
to_show.push_back("roughness")
|
to_show.push_back("roughness")
|
||||||
to_show.push_back("roughness_picker")
|
to_show.push_back("roughness_picker")
|
||||||
to_show.push_back("slope")
|
to_show.push_back("slope")
|
||||||
to_show.push_back("enable_texture")
|
to_show.push_back("texture_filter")
|
||||||
to_show.push_back("margin")
|
to_show.push_back("margin")
|
||||||
to_show.push_back("remove")
|
to_show.push_back("remove")
|
||||||
|
|
||||||
@@ -236,6 +253,7 @@ func _on_tool_changed(p_tool: Terrain3DEditor.Tool, p_operation: Terrain3DEditor
|
|||||||
to_show.push_back("show_cursor_while_painting")
|
to_show.push_back("show_cursor_while_painting")
|
||||||
to_show.push_back("gamma")
|
to_show.push_back("gamma")
|
||||||
to_show.push_back("jitter")
|
to_show.push_back("jitter")
|
||||||
|
to_show.push_back("crosshair_threshold")
|
||||||
tool_settings.show_settings(to_show)
|
tool_settings.show_settings(to_show)
|
||||||
|
|
||||||
operation_builder = null
|
operation_builder = null
|
||||||
@@ -258,9 +276,9 @@ func _on_setting_changed() -> void:
|
|||||||
return
|
return
|
||||||
brush_data = tool_settings.get_settings()
|
brush_data = tool_settings.get_settings()
|
||||||
brush_data["asset_id"] = plugin.asset_dock.get_current_list().get_selected_id()
|
brush_data["asset_id"] = plugin.asset_dock.get_current_list().get_selected_id()
|
||||||
update_decal()
|
|
||||||
plugin.editor.set_brush_data(brush_data)
|
plugin.editor.set_brush_data(brush_data)
|
||||||
plugin.editor.set_operation(_modify_operation(plugin.editor.get_operation()))
|
plugin.editor.set_operation(_modify_operation(plugin.editor.get_operation()))
|
||||||
|
update_decal()
|
||||||
|
|
||||||
|
|
||||||
func update_modifiers() -> void:
|
func update_modifiers() -> void:
|
||||||
@@ -299,195 +317,211 @@ func _invert_operation(p_operation: Terrain3DEditor.Operation, flags: int = OP_N
|
|||||||
|
|
||||||
|
|
||||||
func update_decal() -> void:
|
func update_decal() -> void:
|
||||||
|
if not plugin.terrain or brush_data.size() <= 3:
|
||||||
|
return
|
||||||
|
mat_rid = plugin.terrain.material.get_material_rid()
|
||||||
|
editor_decal_timer.start()
|
||||||
|
|
||||||
# If not a state that should show the decal, hide everything and return
|
# If not a state that should show the decal, hide everything and return
|
||||||
if not visible or \
|
if not visible or \
|
||||||
not plugin.terrain or \
|
|
||||||
plugin._input_mode < 0 or \
|
plugin._input_mode < 0 or \
|
||||||
# Wait for cursor to recenter after moving camera before revealing
|
# Wait for cursor to recenter after moving camera before revealing
|
||||||
# See https://github.com/godotengine/godot/issues/70098
|
# See https://github.com/godotengine/godot/issues/70098
|
||||||
Time.get_ticks_msec() - last_rmb_time <= 30 or \
|
Time.get_ticks_msec() - last_rmb_time <= 30 or \
|
||||||
brush_data.is_empty() or \
|
|
||||||
plugin.editor.get_tool() == Terrain3DEditor.REGION or \
|
|
||||||
(plugin._input_mode > 0 and not brush_data["show_cursor_while_painting"]):
|
(plugin._input_mode > 0 and not brush_data["show_cursor_while_painting"]):
|
||||||
decal.visible = false
|
hide_decal()
|
||||||
for gradient_decal in gradient_decals:
|
|
||||||
gradient_decal.visible = false
|
|
||||||
return
|
return
|
||||||
|
|
||||||
decal.position = plugin.mouse_global_position
|
reset_decal_arrays()
|
||||||
decal.visible = true
|
editor_decal_position[0] = Vector2(plugin.mouse_global_position.x, plugin.mouse_global_position.z)
|
||||||
decal.size = Vector3.ONE * maxf(brush_data["size"], .5)
|
editor_decal_visible[0] = true
|
||||||
|
# Set region size, and modify region map for none background mode.
|
||||||
|
var r_map: PackedInt32Array = plugin.terrain.data.get_region_map()
|
||||||
|
if plugin.editor.get_tool() == Terrain3DEditor.REGION:
|
||||||
|
var r_size: float = float(plugin.terrain.get_region_size()) * plugin.terrain.get_vertex_spacing()
|
||||||
|
var map_size: int = plugin.terrain.data.REGION_MAP_SIZE
|
||||||
|
var half_r_size: float = r_size * 0.5
|
||||||
|
var pos: Vector2 = (Vector2(plugin.mouse_global_position.x, plugin.mouse_global_position.z) +
|
||||||
|
Vector2(half_r_size, half_r_size)).snappedf(r_size) - Vector2(half_r_size, half_r_size)
|
||||||
|
editor_brush_texture_rid = region_texture.get_rid()
|
||||||
|
editor_decal_position[0] = pos
|
||||||
|
editor_decal_size[0] = r_size
|
||||||
|
editor_decal_rotation[0] = 0.0
|
||||||
|
|
||||||
|
var loc: Vector2i = plugin.terrain.data.get_region_location(plugin.mouse_global_position)
|
||||||
|
loc += Vector2i(map_size / 2, map_size / 2)
|
||||||
|
if !(loc.x < 0 or loc.x > map_size - 1 or loc.y < 0 or loc.y > map_size - 1):
|
||||||
|
var index: int = clampi(loc.y * map_size + loc.x, 0, map_size * map_size - 1)
|
||||||
|
if plugin.terrain.material.get_world_background() == Terrain3DMaterial.WorldBackground.NONE:
|
||||||
|
if r_map[index] == 0 and plugin.editor.get_operation() == Terrain3DEditor.ADD:
|
||||||
|
r_map[index] = -index - 1
|
||||||
|
else:
|
||||||
|
r_map[index] = r_map[index]
|
||||||
|
|
||||||
|
match plugin.editor.get_operation():
|
||||||
|
Terrain3DEditor.ADD:
|
||||||
|
if r_map[index] <= 0:
|
||||||
|
editor_decal_color[0] = Color.WHITE
|
||||||
|
editor_decal_color[0].a = 0.25
|
||||||
|
else:
|
||||||
|
hide_decal()
|
||||||
|
|
||||||
|
Terrain3DEditor.SUBTRACT:
|
||||||
|
if r_map[index] > 0:
|
||||||
|
editor_decal_color[0] = Color.WHITE * .15
|
||||||
|
editor_decal_color[0].a = 0.75
|
||||||
|
else:
|
||||||
|
hide_decal()
|
||||||
|
else:
|
||||||
|
hide_decal()
|
||||||
|
# Set texture and color
|
||||||
|
elif picking != Terrain3DEditor.TOOL_MAX:
|
||||||
|
editor_brush_texture_rid = ring_texture.get_rid()
|
||||||
|
editor_decal_size[0] = 10. * plugin.terrain.get_vertex_spacing()
|
||||||
|
match picking:
|
||||||
|
Terrain3DEditor.HEIGHT:
|
||||||
|
editor_decal_color[0] = COLOR_PICK_HEIGHT
|
||||||
|
Terrain3DEditor.COLOR:
|
||||||
|
editor_decal_color[0] = COLOR_PICK_COLOR
|
||||||
|
Terrain3DEditor.ROUGHNESS:
|
||||||
|
editor_decal_color[0] = COLOR_PICK_ROUGH
|
||||||
|
editor_decal_color[0].a = 1.0
|
||||||
|
else:
|
||||||
|
editor_brush_texture_rid = brush_data["brush"][1].get_rid()
|
||||||
|
editor_decal_size[0] = maxf(brush_data["size"], .5)
|
||||||
if brush_data["align_to_view"]:
|
if brush_data["align_to_view"]:
|
||||||
var cam: Camera3D = plugin.terrain.get_camera();
|
var cam: Camera3D = plugin.terrain.get_camera();
|
||||||
if (cam):
|
if (cam):
|
||||||
decal.rotation.y = cam.rotation.y
|
editor_decal_rotation[0] = cam.rotation.y
|
||||||
else:
|
else:
|
||||||
decal.rotation.y = 0
|
editor_decal_rotation[0] = 0.
|
||||||
|
|
||||||
# Set texture and color
|
|
||||||
if picking != Terrain3DEditor.TOOL_MAX:
|
|
||||||
decal.texture_albedo = ring_texture
|
|
||||||
decal.size = Vector3.ONE * 10. * plugin.terrain.get_vertex_spacing()
|
|
||||||
match picking:
|
|
||||||
Terrain3DEditor.HEIGHT:
|
|
||||||
decal.modulate = COLOR_PICK_HEIGHT
|
|
||||||
Terrain3DEditor.COLOR:
|
|
||||||
decal.modulate = COLOR_PICK_COLOR
|
|
||||||
Terrain3DEditor.ROUGHNESS:
|
|
||||||
decal.modulate = COLOR_PICK_ROUGH
|
|
||||||
decal.modulate.a = 1.0
|
|
||||||
else:
|
|
||||||
decal.texture_albedo = brush_data["brush"][1]
|
|
||||||
match plugin.editor.get_tool():
|
match plugin.editor.get_tool():
|
||||||
Terrain3DEditor.SCULPT:
|
Terrain3DEditor.SCULPT:
|
||||||
match plugin.editor.get_operation():
|
match plugin.editor.get_operation():
|
||||||
Terrain3DEditor.ADD:
|
Terrain3DEditor.ADD:
|
||||||
if plugin.modifier_alt:
|
if plugin.modifier_alt:
|
||||||
decal.modulate = COLOR_LIFT
|
editor_decal_color[0] = COLOR_LIFT
|
||||||
decal.modulate.a = clamp(brush_data["strength"], .2, .5)
|
editor_decal_color[0].a = clamp(brush_data["strength"], .2, .5)
|
||||||
else:
|
else:
|
||||||
decal.modulate = COLOR_RAISE
|
editor_decal_color[0] = COLOR_RAISE
|
||||||
decal.modulate.a = clamp(brush_data["strength"], .2, .5)
|
editor_decal_color[0].a = clamp(brush_data["strength"], .25, .5)
|
||||||
Terrain3DEditor.SUBTRACT:
|
Terrain3DEditor.SUBTRACT:
|
||||||
if plugin.modifier_alt:
|
if plugin.modifier_alt:
|
||||||
decal.modulate = COLOR_FLATTEN
|
editor_decal_color[0] = COLOR_FLATTEN
|
||||||
decal.modulate.a = clamp(brush_data["strength"], .2, .5)
|
editor_decal_color[0].a = clamp(brush_data["strength"], .25, .5) + .1
|
||||||
else:
|
else:
|
||||||
decal.modulate = COLOR_LOWER
|
editor_decal_color[0] = COLOR_LOWER
|
||||||
decal.modulate.a = clamp(brush_data["strength"], .2, .5) + .5
|
editor_decal_color[0].a = clamp(brush_data["strength"], .2, .5) + .25
|
||||||
Terrain3DEditor.AVERAGE:
|
Terrain3DEditor.AVERAGE:
|
||||||
decal.modulate = COLOR_SMOOTH
|
editor_decal_color[0] = COLOR_SMOOTH
|
||||||
decal.modulate.a = clamp(brush_data["strength"], .2, .5) + .2
|
editor_decal_color[0].a = clamp(brush_data["strength"], .2, .5) + .25
|
||||||
Terrain3DEditor.GRADIENT:
|
Terrain3DEditor.GRADIENT:
|
||||||
decal.modulate = COLOR_SLOPE
|
editor_decal_color[0] = COLOR_SLOPE
|
||||||
decal.modulate.a = clamp(brush_data["strength"], .2, .5)
|
editor_decal_color[0].a = clamp(brush_data["strength"], .2, .4)
|
||||||
Terrain3DEditor.HEIGHT:
|
Terrain3DEditor.HEIGHT:
|
||||||
decal.modulate = COLOR_HEIGHT
|
editor_decal_color[0] = COLOR_HEIGHT
|
||||||
decal.modulate.a = clamp(brush_data["strength"], .2, .5)
|
editor_decal_color[0].a = clamp(brush_data["strength"], .2, .5) + .25
|
||||||
Terrain3DEditor.TEXTURE:
|
Terrain3DEditor.TEXTURE:
|
||||||
match plugin.editor.get_operation():
|
match plugin.editor.get_operation():
|
||||||
Terrain3DEditor.REPLACE:
|
Terrain3DEditor.REPLACE:
|
||||||
decal.modulate = COLOR_PAINT
|
editor_decal_color[0] = COLOR_PAINT
|
||||||
decal.modulate.a = .7
|
editor_decal_color[0].a = .6
|
||||||
Terrain3DEditor.SUBTRACT:
|
Terrain3DEditor.SUBTRACT:
|
||||||
decal.modulate = COLOR_PAINT
|
editor_decal_color[0] = COLOR_PAINT
|
||||||
decal.modulate.a = clamp(brush_data["strength"], .2, .5)
|
editor_decal_color[0].a = clamp(brush_data["strength"], .2, .5) + .1
|
||||||
Terrain3DEditor.ADD:
|
Terrain3DEditor.ADD:
|
||||||
decal.modulate = COLOR_SPRAY
|
editor_decal_color[0] = COLOR_SPRAY
|
||||||
decal.modulate.a = clamp(brush_data["strength"], .2, .5)
|
editor_decal_color[0].a = clamp(brush_data["strength"], .15, .4)
|
||||||
Terrain3DEditor.COLOR:
|
Terrain3DEditor.COLOR:
|
||||||
decal.modulate = brush_data["color"].srgb_to_linear()
|
editor_decal_color[0] = brush_data["color"].srgb_to_linear()
|
||||||
decal.modulate.a *= clamp(brush_data["strength"], .2, .5)
|
editor_decal_color[0].a *= clamp(brush_data["strength"], .2, .5)
|
||||||
Terrain3DEditor.ROUGHNESS:
|
Terrain3DEditor.ROUGHNESS:
|
||||||
decal.modulate = COLOR_ROUGHNESS
|
editor_decal_color[0] = COLOR_ROUGHNESS
|
||||||
decal.modulate.a = clamp(brush_data["strength"], .2, .5)
|
editor_decal_color[0].a = clamp(brush_data["strength"], .2, .5) + .1
|
||||||
Terrain3DEditor.AUTOSHADER:
|
Terrain3DEditor.AUTOSHADER:
|
||||||
decal.modulate = COLOR_AUTOSHADER
|
editor_decal_color[0] = COLOR_AUTOSHADER
|
||||||
decal.modulate.a = .7
|
editor_decal_color[0].a = .6
|
||||||
Terrain3DEditor.HOLES:
|
Terrain3DEditor.HOLES:
|
||||||
decal.modulate = COLOR_HOLES
|
editor_decal_color[0] = COLOR_HOLES
|
||||||
decal.modulate.a = .85
|
editor_decal_color[0].a = .75
|
||||||
Terrain3DEditor.NAVIGATION:
|
Terrain3DEditor.NAVIGATION:
|
||||||
decal.modulate = COLOR_NAVIGATION
|
editor_decal_color[0] = COLOR_NAVIGATION
|
||||||
decal.modulate.a = .85
|
editor_decal_color[0].a = .80
|
||||||
Terrain3DEditor.INSTANCER:
|
Terrain3DEditor.INSTANCER:
|
||||||
decal.texture_albedo = ring_texture
|
editor_brush_texture_rid = ring_texture.get_rid()
|
||||||
decal.modulate = COLOR_INSTANCER
|
editor_decal_color[0] = COLOR_INSTANCER
|
||||||
decal.modulate.a = 1.0
|
editor_decal_color[0].a = .75
|
||||||
decal.size.y = max(1000, decal.size.y)
|
|
||||||
decal.albedo_mix = 1.0
|
|
||||||
decal.cull_mask = 1 << ( plugin.terrain.get_mouse_layer() - 1 )
|
|
||||||
decal_timer.start()
|
|
||||||
|
|
||||||
for gradient_decal in gradient_decals:
|
editor_decal_visible[1] = false
|
||||||
gradient_decal.visible = false
|
editor_decal_visible[2] = false
|
||||||
|
|
||||||
if plugin.editor.get_operation() == Terrain3DEditor.GRADIENT:
|
if plugin.editor.get_operation() == Terrain3DEditor.GRADIENT:
|
||||||
var index := 0
|
var point1: Vector3 = brush_data["gradient_points"][0]
|
||||||
for point in brush_data["gradient_points"]:
|
if point1 != Vector3.ZERO:
|
||||||
if point != Vector3.ZERO:
|
editor_decal_color[1] = COLOR_SLOPE
|
||||||
var point_decal: Decal = _get_gradient_decal(index)
|
editor_decal_size[1] = 10. * plugin.terrain.get_vertex_spacing()
|
||||||
point_decal.visible = true
|
editor_decal_visible[1] = true
|
||||||
point_decal.position = point
|
editor_decal_position[1] = Vector2(point1.x, point1.z)
|
||||||
index += 1
|
var point2: Vector3 = brush_data["gradient_points"][1]
|
||||||
|
if point2 != Vector3.ZERO:
|
||||||
|
editor_decal_color[2] = COLOR_SLOPE
|
||||||
|
editor_decal_size[2] = 10. * plugin.terrain.get_vertex_spacing()
|
||||||
|
editor_decal_visible[2] = true
|
||||||
|
editor_decal_position[2] = Vector2(point2.x, point2.z)
|
||||||
|
|
||||||
update_compatibility_decal()
|
if RenderingServer.get_current_rendering_method().contains("gl_compatibility"):
|
||||||
|
for i in editor_decal_color.size():
|
||||||
|
editor_decal_color[i].a = maxf(0.1, editor_decal_color[i].a - .25)
|
||||||
|
|
||||||
|
editor_decal_fade = editor_decal_color[0].a
|
||||||
func _get_gradient_decal(index: int) -> Decal:
|
# Update Shader params
|
||||||
if gradient_decals.size() > index:
|
if is_shader_valid():
|
||||||
return gradient_decals[index]
|
RenderingServer.material_set_param(mat_rid, "_editor_brush_texture", editor_brush_texture_rid)
|
||||||
|
RenderingServer.material_set_param(mat_rid, "_editor_ring_texture", editor_ring_texture_rid)
|
||||||
var gradient_decal := Decal.new()
|
|
||||||
gradient_decal = Decal.new()
|
|
||||||
gradient_decal.texture_albedo = ring_texture
|
|
||||||
gradient_decal.modulate = COLOR_SLOPE
|
|
||||||
gradient_decal.size = Vector3.ONE * 10. * plugin.terrain.get_vertex_spacing()
|
|
||||||
gradient_decal.size.y = 1000.
|
|
||||||
gradient_decal.cull_mask = decal.cull_mask
|
|
||||||
add_child(gradient_decal)
|
|
||||||
|
|
||||||
gradient_decals.push_back(gradient_decal)
|
|
||||||
return gradient_decal
|
|
||||||
|
|
||||||
|
|
||||||
func update_compatibility_decal() -> void:
|
|
||||||
if not plugin.terrain.is_compatibility_mode():
|
|
||||||
return
|
|
||||||
|
|
||||||
# Verify setup
|
|
||||||
if editor_decal_position.size() != 3:
|
|
||||||
editor_decal_position.resize(3)
|
|
||||||
editor_decal_rotation.resize(3)
|
|
||||||
editor_decal_size.resize(3)
|
|
||||||
editor_decal_color.resize(3)
|
|
||||||
editor_decal_visible.resize(3)
|
|
||||||
decal_timer.timeout.connect(func():
|
|
||||||
var mat_rid: RID = plugin.terrain.material.get_material_rid()
|
|
||||||
editor_decal_visible[0] = false
|
|
||||||
RenderingServer.material_set_param(mat_rid, "_editor_decal_visible", editor_decal_visible)
|
|
||||||
)
|
|
||||||
|
|
||||||
# Update compatibility decal
|
|
||||||
var mat_rid: RID = plugin.terrain.material.get_material_rid()
|
|
||||||
if decal.visible:
|
|
||||||
editor_decal_position[0] = Vector2(decal.global_position.x, decal.global_position.z)
|
|
||||||
editor_decal_rotation[0] = decal.rotation.y
|
|
||||||
editor_decal_size[0] = brush_data.get("size")
|
|
||||||
editor_decal_color[0] = decal.modulate
|
|
||||||
editor_decal_visible[0] = decal.visible
|
|
||||||
RenderingServer.material_set_param(
|
|
||||||
mat_rid, "_editor_decal_0", decal.texture_albedo.get_rid()
|
|
||||||
)
|
|
||||||
if gradient_decals.size() >= 1:
|
|
||||||
editor_decal_position[1] = Vector2(gradient_decals[0].global_position.x,
|
|
||||||
gradient_decals[0].global_position.z)
|
|
||||||
editor_decal_rotation[1] = gradient_decals[0].rotation.y
|
|
||||||
editor_decal_size[1] = 10.0
|
|
||||||
editor_decal_color[1] = gradient_decals[0].modulate
|
|
||||||
editor_decal_visible[1] = gradient_decals[0].visible
|
|
||||||
RenderingServer.material_set_param(
|
|
||||||
mat_rid, "_editor_decal_1", gradient_decals[0].texture_albedo.get_rid()
|
|
||||||
)
|
|
||||||
if gradient_decals.size() >= 2:
|
|
||||||
editor_decal_position[2] = Vector2(gradient_decals[1].global_position.x,
|
|
||||||
gradient_decals[1].global_position.z)
|
|
||||||
editor_decal_rotation[2] = gradient_decals[1].rotation.y
|
|
||||||
editor_decal_size[2] = 10.0
|
|
||||||
editor_decal_color[2] = gradient_decals[1].modulate
|
|
||||||
editor_decal_visible[2] = gradient_decals[1].visible
|
|
||||||
RenderingServer.material_set_param(
|
|
||||||
mat_rid, "_editor_decal_2", gradient_decals[1].texture_albedo.get_rid()
|
|
||||||
)
|
|
||||||
RenderingServer.material_set_param(mat_rid, "_editor_decal_position", editor_decal_position)
|
RenderingServer.material_set_param(mat_rid, "_editor_decal_position", editor_decal_position)
|
||||||
RenderingServer.material_set_param(mat_rid, "_editor_decal_rotation", editor_decal_rotation)
|
RenderingServer.material_set_param(mat_rid, "_editor_decal_rotation", editor_decal_rotation)
|
||||||
RenderingServer.material_set_param(mat_rid, "_editor_decal_size", editor_decal_size)
|
RenderingServer.material_set_param(mat_rid, "_editor_decal_size", editor_decal_size)
|
||||||
RenderingServer.material_set_param(mat_rid, "_editor_decal_color", editor_decal_color)
|
RenderingServer.material_set_param(mat_rid, "_editor_decal_color", editor_decal_color)
|
||||||
RenderingServer.material_set_param(mat_rid, "_editor_decal_visible", editor_decal_visible)
|
RenderingServer.material_set_param(mat_rid, "_editor_decal_visible", editor_decal_visible)
|
||||||
|
RenderingServer.material_set_param(mat_rid, "_editor_crosshair_threshold", brush_data["crosshair_threshold"] + 0.1)
|
||||||
|
RenderingServer.material_set_param(mat_rid, "_region_map", r_map)
|
||||||
|
|
||||||
|
|
||||||
|
func is_shader_valid() -> bool:
|
||||||
|
# As long as the compiled shader contains at least 1 uniform, we can use it to check
|
||||||
|
# if the shader compilation has failed, as this will then return an empty dictionary.
|
||||||
|
if not plugin.terrain:
|
||||||
|
return false
|
||||||
|
var params = RenderingServer.get_shader_parameter_list(plugin.terrain.material.get_shader_rid())
|
||||||
|
if params.is_empty():
|
||||||
|
return false
|
||||||
|
else:
|
||||||
|
return true
|
||||||
|
|
||||||
|
|
||||||
|
func hide_decal() -> void:
|
||||||
|
editor_decal_visible = [false, false, false]
|
||||||
|
if is_shader_valid():
|
||||||
|
var r_map: PackedInt32Array = plugin.terrain.data.get_region_map()
|
||||||
|
RenderingServer.material_set_param(mat_rid, "_editor_decal_visible", editor_decal_visible)
|
||||||
|
RenderingServer.material_set_param(mat_rid, "_region_map", r_map)
|
||||||
|
|
||||||
|
|
||||||
|
# These array sizes are reset to 0 when closing scenes for some unknown reason, so check and reset
|
||||||
|
func reset_decal_arrays() -> void:
|
||||||
|
if editor_decal_color.size() < 3:
|
||||||
|
editor_decal_position = [Vector2(), Vector2(), Vector2()]
|
||||||
|
editor_decal_rotation = [float(), float(), float()]
|
||||||
|
editor_decal_size = [float(), float(), float()]
|
||||||
|
editor_decal_color = [Color(), Color(), Color()]
|
||||||
|
editor_decal_visible = [false, false, false]
|
||||||
|
editor_brush_texture_rid = RID()
|
||||||
|
|
||||||
|
|
||||||
func set_decal_rotation(p_rot: float) -> void:
|
func set_decal_rotation(p_rot: float) -> void:
|
||||||
decal.rotation.y = p_rot
|
editor_decal_rotation[0] = p_rot
|
||||||
|
if is_shader_valid():
|
||||||
|
RenderingServer.material_set_param(mat_rid, "_editor_decal_rotation", editor_decal_rotation)
|
||||||
|
|
||||||
|
|
||||||
func _on_picking(p_type: int, p_callback: Callable) -> void:
|
func _on_picking(p_type: int, p_callback: Callable) -> void:
|
||||||
@@ -515,7 +549,7 @@ func pick(p_global_position: Vector3) -> void:
|
|||||||
var color: Color
|
var color: Color
|
||||||
match picking:
|
match picking:
|
||||||
Terrain3DEditor.HEIGHT, Terrain3DEditor.SCULPT:
|
Terrain3DEditor.HEIGHT, Terrain3DEditor.SCULPT:
|
||||||
color = plugin.terrain.data.get_pixel(Terrain3DRegion.TYPE_HEIGHT, p_global_position)
|
color = Color(plugin.terrain.data.get_height(p_global_position), 0., 0., 1.)
|
||||||
Terrain3DEditor.ROUGHNESS:
|
Terrain3DEditor.ROUGHNESS:
|
||||||
color = plugin.terrain.data.get_pixel(Terrain3DRegion.TYPE_COLOR, p_global_position)
|
color = plugin.terrain.data.get_pixel(Terrain3DRegion.TYPE_COLOR, p_global_position)
|
||||||
Terrain3DEditor.COLOR:
|
Terrain3DEditor.COLOR:
|
||||||
@@ -527,7 +561,9 @@ func pick(p_global_position: Vector3) -> void:
|
|||||||
_:
|
_:
|
||||||
push_error("Unsupported picking type: ", picking)
|
push_error("Unsupported picking type: ", picking)
|
||||||
return
|
return
|
||||||
|
if picking_callback.is_valid():
|
||||||
picking_callback.call(picking, color, p_global_position)
|
picking_callback.call(picking, color, p_global_position)
|
||||||
|
picking_callback = Callable()
|
||||||
picking = Terrain3DEditor.TOOL_MAX
|
picking = Terrain3DEditor.TOOL_MAX
|
||||||
|
|
||||||
elif operation_builder and operation_builder.is_picking():
|
elif operation_builder and operation_builder.is_picking():
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
uid://bv4lj2cvubl7p
|
uid://bpad72s36mwkx
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
[configuration]
|
[configuration]
|
||||||
|
|
||||||
entry_symbol = "terrain_3d_init"
|
entry_symbol = "terrain_3d_init"
|
||||||
compatibility_minimum = 4.2
|
compatibility_minimum = 4.4
|
||||||
|
|
||||||
[icons]
|
[icons]
|
||||||
|
|
||||||
@@ -27,3 +27,6 @@ android.release.arm64 = "res://addons/terrain_3d/bin/libterrain.android.release.
|
|||||||
|
|
||||||
ios.debug = "res://addons/terrain_3d/bin/libterrain.ios.debug.universal.dylib"
|
ios.debug = "res://addons/terrain_3d/bin/libterrain.ios.debug.universal.dylib"
|
||||||
ios.release = "res://addons/terrain_3d/bin/libterrain.ios.release.universal.dylib"
|
ios.release = "res://addons/terrain_3d/bin/libterrain.ios.release.universal.dylib"
|
||||||
|
|
||||||
|
web.debug = "res://addons/terrain_3d/bin/libterrain.web.debug.wasm32.wasm"
|
||||||
|
web.release = "res://addons/terrain_3d/bin/libterrain.web.release.wasm32.wasm"
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
uid://chcl8jili5ggt
|
uid://bdn2ygldx56a8
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
# Copyright © 2025 Cory Petkovsek, Roope Palmroos, and Contributors.
|
||||||
|
# Importer for Terrain3D
|
||||||
@tool
|
@tool
|
||||||
extends Terrain3D
|
extends Terrain3D
|
||||||
|
|
||||||
@@ -20,10 +22,14 @@ func reset_settings(p_value) -> void:
|
|||||||
r16_size = Vector2i(1024, 1024)
|
r16_size = Vector2i(1024, 1024)
|
||||||
material = null
|
material = null
|
||||||
assets = null
|
assets = null
|
||||||
|
reset_terrain(true)
|
||||||
|
|
||||||
|
|
||||||
func reset_terrain(p_value) -> void:
|
func reset_terrain(p_value) -> void:
|
||||||
data_directory = ""
|
data_directory = ""
|
||||||
|
for region:Terrain3DRegion in data.get_regions_active():
|
||||||
|
data.remove_region(region, false)
|
||||||
|
data.update_maps(Terrain3DRegion.TYPE_MAX, true, false)
|
||||||
|
|
||||||
|
|
||||||
func update_heights(p_value) -> void:
|
func update_heights(p_value) -> void:
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
uid://co2gd0uhumg34
|
uid://cib2rig7vup10
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[gd_scene load_steps=9 format=3 uid="uid://blaieaqp413k7"]
|
[gd_scene load_steps=9 format=3 uid="uid://blaieaqp413k7"]
|
||||||
|
|
||||||
[ext_resource type="Script" uid="uid://co2gd0uhumg34" path="res://addons/terrain_3d/tools/importer.gd" id="1_60b8f"]
|
[ext_resource type="Script" path="res://addons/terrain_3d/tools/importer.gd" id="1_60b8f"]
|
||||||
|
|
||||||
[sub_resource type="Gradient" id="Gradient_88f3t"]
|
[sub_resource type="Gradient" id="Gradient_88f3t"]
|
||||||
offsets = PackedFloat32Array(0.2, 1)
|
offsets = PackedFloat32Array(0.2, 1)
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
# Copyright © 2025 Cory Petkovsek, Roope Palmroos, and Contributors.
|
||||||
|
# Objects parent for Terrain3D
|
||||||
|
# Children nodes get transform updates on sculpting
|
||||||
@tool
|
@tool
|
||||||
extends Node3D
|
extends Node3D
|
||||||
class_name Terrain3DObjects
|
class_name Terrain3DObjects
|
||||||
@@ -97,6 +100,7 @@ func _on_child_exiting_tree(p_node: Node) -> void:
|
|||||||
|
|
||||||
var helper: TransformChangedNotifier = p_node.get_node_or_null(CHILD_HELPER_PATH)
|
var helper: TransformChangedNotifier = p_node.get_node_or_null(CHILD_HELPER_PATH)
|
||||||
if helper:
|
if helper:
|
||||||
|
if helper.transform_changed.is_connected(_on_child_transform_changed):
|
||||||
helper.transform_changed.disconnect(_on_child_transform_changed)
|
helper.transform_changed.disconnect(_on_child_transform_changed)
|
||||||
p_node.remove_child(helper)
|
p_node.remove_child(helper)
|
||||||
helper.queue_free()
|
helper.queue_free()
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
uid://cl5vs8p47r15v
|
uid://dbndw8p05yam7
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
# Copyright © 2025 Cory Petkovsek, Roope Palmroos, and Contributors.
|
||||||
|
# Transform Changed Notifier for Terrain3D
|
||||||
@tool
|
@tool
|
||||||
extends Node3D
|
extends Node3D
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
uid://drw381usdrd8b
|
uid://claxtgppe8keq
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -34,7 +34,10 @@ ssao_enabled = true
|
|||||||
ssil_enabled = true
|
ssil_enabled = true
|
||||||
sdfgi_enabled = true
|
sdfgi_enabled = true
|
||||||
glow_enabled = true
|
glow_enabled = true
|
||||||
fog_density = 0.0005
|
fog_mode = 1
|
||||||
|
fog_density = 0.1
|
||||||
|
volumetric_fog_density = 0.006
|
||||||
|
volumetric_fog_albedo = Color(0.530013, 0.696854, 0.82526, 1)
|
||||||
|
|
||||||
[node name="Realtime Day Night Cycle" type="Node3D"]
|
[node name="Realtime Day Night Cycle" type="Node3D"]
|
||||||
script = ExtResource("1_idjmm")
|
script = ExtResource("1_idjmm")
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
[gd_scene load_steps=8 format=3 uid="uid://cofnhdcclon1w"]
|
[gd_scene load_steps=8 format=3 uid="uid://cofnhdcclon1w"]
|
||||||
|
|
||||||
[ext_resource type="Script" uid="uid://dd26e3lk8oe1l" path="res://demo/src/CodeGenerated.gd" id="1_h7vyv"]
|
[ext_resource type="Script" uid="uid://dakis6gu8b7nm" path="res://demo/src/CodeGenerated.gd" id="1_h7vyv"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://domhm87hbhbg1" path="res://demo/components/Player.tscn" id="2_3v2uf"]
|
||||||
[ext_resource type="PackedScene" uid="uid://bb2lp50sjndus" path="res://demo/components/Environment.tscn" id="3_71ikj"]
|
[ext_resource type="PackedScene" uid="uid://bb2lp50sjndus" path="res://demo/components/Environment.tscn" id="3_71ikj"]
|
||||||
[ext_resource type="PackedScene" uid="uid://di5fovhcyd7re" path="res://demo/components/Enemy.tscn" id="4_p8qry"]
|
[ext_resource type="PackedScene" uid="uid://di5fovhcyd7re" path="res://demo/components/Enemy.tscn" id="4_p8qry"]
|
||||||
[ext_resource type="PackedScene" uid="uid://d2jihfohphuue" path="res://demo/components/UI.tscn" id="4_x5ge4"]
|
[ext_resource type="PackedScene" uid="uid://d2jihfohphuue" path="res://demo/components/UI.tscn" id="4_x5ge4"]
|
||||||
[ext_resource type="Script" uid="uid://br3a8kjyuwoeg" path="res://demo/src/RuntimeNavigationBaker.gd" id="5_445ur"]
|
[ext_resource type="Script" uid="uid://brh8x1wnycrl5" path="res://demo/src/RuntimeNavigationBaker.gd" id="5_445ur"]
|
||||||
|
|
||||||
[sub_resource type="Resource" id="Resource_mekfw"]
|
|
||||||
metadata/__load_path__ = "res://demo/components/Player.tscn"
|
|
||||||
|
|
||||||
[sub_resource type="NavigationMesh" id="NavigationMesh_vs6am"]
|
[sub_resource type="NavigationMesh" id="NavigationMesh_vs6am"]
|
||||||
geometry_parsed_geometry_type = 1
|
geometry_parsed_geometry_type = 1
|
||||||
@@ -19,7 +17,7 @@ script = ExtResource("1_h7vyv")
|
|||||||
|
|
||||||
[node name="Environment" parent="." instance=ExtResource("3_71ikj")]
|
[node name="Environment" parent="." instance=ExtResource("3_71ikj")]
|
||||||
|
|
||||||
[node name="Player" parent="."]
|
[node name="Player" parent="." instance=ExtResource("2_3v2uf")]
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 50, 0)
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 50, 0)
|
||||||
|
|
||||||
[node name="Enemy" parent="." node_paths=PackedStringArray("target") instance=ExtResource("4_p8qry")]
|
[node name="Enemy" parent="." node_paths=PackedStringArray("target") instance=ExtResource("4_p8qry")]
|
||||||
|
|||||||
@@ -1,15 +1,13 @@
|
|||||||
[gd_scene load_steps=12 format=3 uid="uid://chol2xlfbq7cu"]
|
[gd_scene load_steps=12 format=3 uid="uid://chol2xlfbq7cu"]
|
||||||
|
|
||||||
[ext_resource type="Script" uid="uid://8atelq27qxnq" path="res://demo/src/DemoScene.gd" id="1_k7qca"]
|
[ext_resource type="Script" uid="uid://chstoagn42gbr" path="res://demo/src/DemoScene.gd" id="1_k7qca"]
|
||||||
[ext_resource type="PackedScene" uid="uid://d2jihfohphuue" path="res://demo/components/UI.tscn" id="2_nqak5"]
|
[ext_resource type="PackedScene" uid="uid://d2jihfohphuue" path="res://demo/components/UI.tscn" id="2_nqak5"]
|
||||||
[ext_resource type="PackedScene" uid="uid://dwnhqfjq7v1pq" path="res://demo/components/Borders.tscn" id="3_cw38j"]
|
[ext_resource type="PackedScene" uid="uid://dwnhqfjq7v1pq" path="res://demo/components/Borders.tscn" id="3_cw38j"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://domhm87hbhbg1" path="res://demo/components/Player.tscn" id="3_ht63y"]
|
||||||
[ext_resource type="PackedScene" uid="uid://djhl3foqkj4e2" path="res://demo/components/Tunnel.tscn" id="3_kdh0b"]
|
[ext_resource type="PackedScene" uid="uid://djhl3foqkj4e2" path="res://demo/components/Tunnel.tscn" id="3_kdh0b"]
|
||||||
[ext_resource type="PackedScene" uid="uid://bb2lp50sjndus" path="res://demo/components/Environment.tscn" id="3_yqldq"]
|
[ext_resource type="PackedScene" uid="uid://bb2lp50sjndus" path="res://demo/components/Environment.tscn" id="3_yqldq"]
|
||||||
[ext_resource type="Terrain3DAssets" uid="uid://dal3jhw6241qg" path="res://demo/data/assets.tres" id="8_g2of2"]
|
[ext_resource type="Terrain3DAssets" uid="uid://dal3jhw6241qg" path="res://demo/data/assets.tres" id="8_g2of2"]
|
||||||
|
|
||||||
[sub_resource type="Resource" id="Resource_wlawv"]
|
|
||||||
metadata/__load_path__ = "res://demo/components/Player.tscn"
|
|
||||||
|
|
||||||
[sub_resource type="Gradient" id="Gradient_vr1m7"]
|
[sub_resource type="Gradient" id="Gradient_vr1m7"]
|
||||||
offsets = PackedFloat32Array(0.2, 1)
|
offsets = PackedFloat32Array(0.2, 1)
|
||||||
colors = PackedColorArray(1, 1, 1, 1, 0, 0, 0, 1)
|
colors = PackedColorArray(1, 1, 1, 1, 0, 0, 0, 1)
|
||||||
@@ -35,32 +33,41 @@ noise = SubResource("FastNoiseLite_d8lcj")
|
|||||||
_shader_parameters = {
|
_shader_parameters = {
|
||||||
"_mouse_layer": 2147483648,
|
"_mouse_layer": 2147483648,
|
||||||
"auto_base_texture": 0,
|
"auto_base_texture": 0,
|
||||||
"auto_height_reduction": 0.1,
|
"auto_height_reduction": 0.0,
|
||||||
"auto_overlay_texture": 1,
|
"auto_overlay_texture": 1,
|
||||||
"auto_slope": 1.0,
|
"auto_slope": 1.0,
|
||||||
|
"bias_distance": 512.0,
|
||||||
"blend_sharpness": 0.87,
|
"blend_sharpness": 0.87,
|
||||||
|
"depth_blur": 0.0,
|
||||||
"dual_scale_far": 170.0,
|
"dual_scale_far": 170.0,
|
||||||
"dual_scale_near": 100.0,
|
"dual_scale_near": 100.0,
|
||||||
"dual_scale_reduction": 0.3,
|
"dual_scale_reduction": 0.3,
|
||||||
"dual_scale_texture": 0,
|
"dual_scale_texture": 0,
|
||||||
|
"enable_macro_variation": true,
|
||||||
|
"enable_projection": true,
|
||||||
"height_blending": true,
|
"height_blending": true,
|
||||||
"macro_variation1": Color(0.878431, 0.862745, 0.901961, 1),
|
"macro_variation1": Color(0.855, 0.8625, 0.9, 1),
|
||||||
"macro_variation2": Color(0.898039, 0.898039, 0.803922, 1),
|
"macro_variation2": Color(0.9, 0.885, 0.81, 1),
|
||||||
|
"macro_variation_slope": 0.333,
|
||||||
|
"mipmap_bias": 1.0,
|
||||||
"noise1_angle": 0.1,
|
"noise1_angle": 0.1,
|
||||||
"noise1_offset": Vector2(0.5, 0.5),
|
"noise1_offset": Vector2(0.5, 0.5),
|
||||||
"noise1_scale": 0.04,
|
"noise1_scale": 0.04,
|
||||||
"noise2_scale": 0.076,
|
"noise2_scale": 0.076,
|
||||||
"noise3_scale": 0.225,
|
"noise3_scale": 0.225,
|
||||||
"noise_texture": SubResource("NoiseTexture2D_bov7h"),
|
"noise_texture": SubResource("NoiseTexture2D_bov7h"),
|
||||||
|
"projection_angular_division": 1.0,
|
||||||
|
"projection_threshold": 0.8,
|
||||||
"tri_scale_reduction": 0.3,
|
"tri_scale_reduction": 0.3,
|
||||||
"vertex_normals_distance": 128.0,
|
"world_noise_fragment_normals": false,
|
||||||
"world_noise_height": 34.0,
|
"world_noise_height": 34.0,
|
||||||
"world_noise_lod_distance": 7500.0,
|
"world_noise_lod_distance": 7500.0,
|
||||||
"world_noise_max_octaves": 4,
|
"world_noise_max_octaves": 4,
|
||||||
"world_noise_min_octaves": 2,
|
"world_noise_min_octaves": 2,
|
||||||
"world_noise_offset": Vector3(2.17, -1.225, 1.9),
|
"world_noise_offset": Vector3(2.17, -1.225, 1.9),
|
||||||
"world_noise_region_blend": 0.33,
|
"world_noise_region_blend": 0.33,
|
||||||
"world_noise_scale": 9.85
|
"world_noise_scale": 9.85,
|
||||||
|
"world_space_normal_blend": true
|
||||||
}
|
}
|
||||||
world_background = 2
|
world_background = 2
|
||||||
auto_shader = true
|
auto_shader = true
|
||||||
@@ -79,7 +86,7 @@ collision_mask = 3
|
|||||||
|
|
||||||
[node name="Tunnel" parent="." instance=ExtResource("3_kdh0b")]
|
[node name="Tunnel" parent="." instance=ExtResource("3_kdh0b")]
|
||||||
|
|
||||||
[node name="Player" parent="."]
|
[node name="Player" parent="." instance=ExtResource("3_ht63y")]
|
||||||
transform = Transform3D(0.176947, 0, -0.98422, 0, 1, 0, 0.98422, 0, 0.176947, 223.143, 105.348, -1833.08)
|
transform = Transform3D(0.176947, 0, -0.98422, 0, 1, 0, 0.98422, 0, 0.176947, 223.143, 105.348, -1833.08)
|
||||||
|
|
||||||
[node name="Terrain3D" type="Terrain3D" parent="."]
|
[node name="Terrain3D" type="Terrain3D" parent="."]
|
||||||
|
|||||||
@@ -1,17 +1,15 @@
|
|||||||
[gd_scene load_steps=14 format=3 uid="uid://x34e4v60vdmy"]
|
[gd_scene load_steps=14 format=3 uid="uid://x34e4v60vdmy"]
|
||||||
|
|
||||||
[ext_resource type="Script" uid="uid://8atelq27qxnq" path="res://demo/src/DemoScene.gd" id="1_po4vw"]
|
[ext_resource type="Script" path="res://demo/src/DemoScene.gd" id="1_po4vw"]
|
||||||
[ext_resource type="PackedScene" uid="uid://bb2lp50sjndus" path="res://demo/components/Environment.tscn" id="2_i74wg"]
|
[ext_resource type="PackedScene" uid="uid://bb2lp50sjndus" path="res://demo/components/Environment.tscn" id="2_i74wg"]
|
||||||
[ext_resource type="PackedScene" uid="uid://dwnhqfjq7v1pq" path="res://demo/components/Borders.tscn" id="3_3upu0"]
|
[ext_resource type="PackedScene" uid="uid://dwnhqfjq7v1pq" path="res://demo/components/Borders.tscn" id="3_3upu0"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://domhm87hbhbg1" path="res://demo/components/Player.tscn" id="4_fk3ul"]
|
||||||
[ext_resource type="PackedScene" uid="uid://djhl3foqkj4e2" path="res://demo/components/Tunnel.tscn" id="5_2lvlr"]
|
[ext_resource type="PackedScene" uid="uid://djhl3foqkj4e2" path="res://demo/components/Tunnel.tscn" id="5_2lvlr"]
|
||||||
[ext_resource type="PackedScene" uid="uid://d2jihfohphuue" path="res://demo/components/UI.tscn" id="5_rc7e2"]
|
[ext_resource type="PackedScene" uid="uid://d2jihfohphuue" path="res://demo/components/UI.tscn" id="5_rc7e2"]
|
||||||
[ext_resource type="NavigationMesh" uid="uid://yf3fu23666k8" path="res://demo/data/nav_mesh.res" id="8_6jfdr"]
|
[ext_resource type="NavigationMesh" uid="uid://yf3fu23666k8" path="res://demo/data/nav_mesh.res" id="8_6jfdr"]
|
||||||
[ext_resource type="Terrain3DAssets" uid="uid://dal3jhw6241qg" path="res://demo/data/assets.tres" id="10_qmhh8"]
|
[ext_resource type="Terrain3DAssets" uid="uid://dal3jhw6241qg" path="res://demo/data/assets.tres" id="10_qmhh8"]
|
||||||
[ext_resource type="PackedScene" uid="uid://di5fovhcyd7re" path="res://demo/components/Enemy.tscn" id="12_b2xl8"]
|
[ext_resource type="PackedScene" uid="uid://di5fovhcyd7re" path="res://demo/components/Enemy.tscn" id="12_b2xl8"]
|
||||||
|
|
||||||
[sub_resource type="Resource" id="Resource_it78x"]
|
|
||||||
metadata/__load_path__ = "res://demo/components/Player.tscn"
|
|
||||||
|
|
||||||
[sub_resource type="Gradient" id="Gradient_vr1m7"]
|
[sub_resource type="Gradient" id="Gradient_vr1m7"]
|
||||||
offsets = PackedFloat32Array(0.2, 1)
|
offsets = PackedFloat32Array(0.2, 1)
|
||||||
colors = PackedColorArray(1, 1, 1, 1, 0, 0, 0, 1)
|
colors = PackedColorArray(1, 1, 1, 1, 0, 0, 0, 1)
|
||||||
@@ -40,22 +38,29 @@ _shader_parameters = {
|
|||||||
"auto_height_reduction": 0.1,
|
"auto_height_reduction": 0.1,
|
||||||
"auto_overlay_texture": 1,
|
"auto_overlay_texture": 1,
|
||||||
"auto_slope": 1.0,
|
"auto_slope": 1.0,
|
||||||
|
"bias_distance": 512.0,
|
||||||
"blend_sharpness": 0.87,
|
"blend_sharpness": 0.87,
|
||||||
|
"depth_blur": 0.0,
|
||||||
"dual_scale_far": 170.0,
|
"dual_scale_far": 170.0,
|
||||||
"dual_scale_near": 100.0,
|
"dual_scale_near": 100.0,
|
||||||
"dual_scale_reduction": 0.3,
|
"dual_scale_reduction": 0.3,
|
||||||
"dual_scale_texture": 0,
|
"dual_scale_texture": 0,
|
||||||
|
"enable_macro_variation": true,
|
||||||
|
"enable_projection": true,
|
||||||
"height_blending": true,
|
"height_blending": true,
|
||||||
"macro_variation1": Color(0.878431, 0.862745, 0.901961, 1),
|
"macro_variation1": Color(0.878431, 0.862745, 0.901961, 1),
|
||||||
"macro_variation2": Color(0.898039, 0.898039, 0.803922, 1),
|
"macro_variation2": Color(0.898039, 0.898039, 0.803922, 1),
|
||||||
|
"macro_variation_slope": 0.333,
|
||||||
|
"mipmap_bias": 1.0,
|
||||||
"noise1_angle": 0.1,
|
"noise1_angle": 0.1,
|
||||||
"noise1_offset": Vector2(0.5, 0.5),
|
"noise1_offset": Vector2(0.5, 0.5),
|
||||||
"noise1_scale": 0.04,
|
"noise1_scale": 0.04,
|
||||||
"noise2_scale": 0.076,
|
"noise2_scale": 0.076,
|
||||||
"noise3_scale": 0.225,
|
"noise3_scale": 0.225,
|
||||||
"noise_texture": SubResource("NoiseTexture2D_bov7h"),
|
"noise_texture": SubResource("NoiseTexture2D_bov7h"),
|
||||||
"tri_scale_reduction": 0.3,
|
"projection_angular_division": 1.436,
|
||||||
"vertex_normals_distance": 128.0
|
"projection_threshold": 0.8,
|
||||||
|
"tri_scale_reduction": 0.3
|
||||||
}
|
}
|
||||||
world_background = 0
|
world_background = 0
|
||||||
auto_shader = true
|
auto_shader = true
|
||||||
@@ -73,7 +78,7 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 512, -2.5, 512)
|
|||||||
|
|
||||||
[node name="Tunnel" parent="." instance=ExtResource("5_2lvlr")]
|
[node name="Tunnel" parent="." instance=ExtResource("5_2lvlr")]
|
||||||
|
|
||||||
[node name="Player" parent="."]
|
[node name="Player" parent="." instance=ExtResource("4_fk3ul")]
|
||||||
transform = Transform3D(0.0774673, 0, -0.996995, 0, 1, 0, 0.996995, 0, 0.0774673, 229.418, 104.956, -1838.16)
|
transform = Transform3D(0.0774673, 0, -0.996995, 0, 1, 0, 0.996995, 0, 0.0774673, 229.418, 104.956, -1838.16)
|
||||||
|
|
||||||
[node name="Enemy" parent="." node_paths=PackedStringArray("target") instance=ExtResource("12_b2xl8")]
|
[node name="Enemy" parent="." node_paths=PackedStringArray("target") instance=ExtResource("12_b2xl8")]
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
[gd_resource type="StandardMaterial3D" load_steps=3 format=3 uid="uid://fkhkir6t1hml"]
|
[gd_resource type="StandardMaterial3D" load_steps=2 format=3 uid="uid://fkhkir6t1hml"]
|
||||||
|
|
||||||
[ext_resource type="Texture2D" uid="uid://br7bfxcxh60df" path="res://demo/assets/textures/rock030_alb_ht.png" id="1_ahc02"]
|
[ext_resource type="Texture2D" uid="uid://c307hdmos4gtm" path="res://demo/assets/textures/rock023_nrm_rgh.png" id="2_3is54"]
|
||||||
[ext_resource type="Texture2D" uid="uid://cr72kjil43vgj" path="res://demo/assets/textures/rock030_nrm_rgh.png" id="2_glxvb"]
|
|
||||||
|
|
||||||
[resource]
|
[resource]
|
||||||
transparency = 1
|
transparency = 1
|
||||||
@@ -9,19 +8,18 @@ vertex_color_use_as_albedo = true
|
|||||||
albedo_color = Color(0.4, 1, 1, 0.647059)
|
albedo_color = Color(0.4, 1, 1, 0.647059)
|
||||||
metallic_specular = 1.0
|
metallic_specular = 1.0
|
||||||
roughness = 0.2
|
roughness = 0.2
|
||||||
roughness_texture = ExtResource("2_glxvb")
|
roughness_texture = ExtResource("2_3is54")
|
||||||
roughness_texture_channel = 3
|
roughness_texture_channel = 3
|
||||||
emission_enabled = true
|
emission_enabled = true
|
||||||
emission = Color(0, 0.145098, 0.776471, 1)
|
emission = Color(0, 0.145098, 0.776471, 1)
|
||||||
emission_energy_multiplier = 1.4
|
emission_energy_multiplier = 1.4
|
||||||
normal_enabled = true
|
normal_enabled = true
|
||||||
normal_scale = 0.4
|
normal_scale = 0.4
|
||||||
normal_texture = ExtResource("2_glxvb")
|
normal_texture = ExtResource("2_3is54")
|
||||||
rim_enabled = true
|
rim_enabled = true
|
||||||
rim = 0.25
|
rim = 0.25
|
||||||
clearcoat_roughness = 0.0
|
clearcoat_roughness = 0.0
|
||||||
ao_light_affect = 1.0
|
ao_light_affect = 1.0
|
||||||
ao_texture = ExtResource("1_ahc02")
|
|
||||||
ao_texture_channel = 3
|
ao_texture_channel = 3
|
||||||
refraction_enabled = true
|
refraction_enabled = true
|
||||||
refraction_scale = -0.1
|
refraction_scale = -0.1
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
[gd_resource type="StandardMaterial3D" load_steps=3 format=3 uid="uid://cso4f2iyuxpmc"]
|
[gd_resource type="StandardMaterial3D" load_steps=2 format=3 uid="uid://cso4f2iyuxpmc"]
|
||||||
|
|
||||||
[ext_resource type="Texture2D" uid="uid://br7bfxcxh60df" path="res://demo/assets/textures/rock030_alb_ht.png" id="1_uwd12"]
|
[ext_resource type="Texture2D" uid="uid://c307hdmos4gtm" path="res://demo/assets/textures/rock023_nrm_rgh.png" id="2_1t610"]
|
||||||
[ext_resource type="Texture2D" uid="uid://cr72kjil43vgj" path="res://demo/assets/textures/rock030_nrm_rgh.png" id="2_0wyvn"]
|
|
||||||
|
|
||||||
[resource]
|
[resource]
|
||||||
transparency = 1
|
transparency = 1
|
||||||
@@ -9,18 +8,17 @@ vertex_color_use_as_albedo = true
|
|||||||
albedo_color = Color(0.4, 0.4, 1, 0.556863)
|
albedo_color = Color(0.4, 0.4, 1, 0.556863)
|
||||||
metallic_specular = 1.0
|
metallic_specular = 1.0
|
||||||
roughness = 0.2
|
roughness = 0.2
|
||||||
roughness_texture = ExtResource("2_0wyvn")
|
roughness_texture = ExtResource("2_1t610")
|
||||||
roughness_texture_channel = 3
|
roughness_texture_channel = 3
|
||||||
emission_enabled = true
|
emission_enabled = true
|
||||||
emission = Color(0.235294, 0.145098, 0.776471, 1)
|
emission = Color(0.235294, 0.145098, 0.776471, 1)
|
||||||
normal_enabled = true
|
normal_enabled = true
|
||||||
normal_scale = 0.4
|
normal_scale = 0.4
|
||||||
normal_texture = ExtResource("2_0wyvn")
|
normal_texture = ExtResource("2_1t610")
|
||||||
rim_enabled = true
|
rim_enabled = true
|
||||||
rim = 0.25
|
rim = 0.25
|
||||||
clearcoat_roughness = 0.0
|
clearcoat_roughness = 0.0
|
||||||
ao_light_affect = 1.0
|
ao_light_affect = 1.0
|
||||||
ao_texture = ExtResource("1_uwd12")
|
|
||||||
ao_texture_channel = 3
|
ao_texture_channel = 3
|
||||||
refraction_enabled = true
|
refraction_enabled = true
|
||||||
refraction_scale = -0.1
|
refraction_scale = -0.1
|
||||||
|
|||||||
24
demo/assets/materials/M_crystal_red.tres
Normal file
24
demo/assets/materials/M_crystal_red.tres
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
[gd_resource type="StandardMaterial3D" load_steps=2 format=3 uid="uid://ickkffutwcvo"]
|
||||||
|
|
||||||
|
[ext_resource type="Texture2D" uid="uid://c307hdmos4gtm" path="res://demo/assets/textures/rock023_nrm_rgh.png" id="2_k2yhv"]
|
||||||
|
|
||||||
|
[resource]
|
||||||
|
transparency = 1
|
||||||
|
albedo_color = Color(0.670588, 0.0588235, 0.384314, 0.509804)
|
||||||
|
metallic_specular = 1.0
|
||||||
|
roughness = 0.2
|
||||||
|
roughness_texture = ExtResource("2_k2yhv")
|
||||||
|
roughness_texture_channel = 3
|
||||||
|
emission_enabled = true
|
||||||
|
emission = Color(0.258824, 0.0823529, 0.25098, 1)
|
||||||
|
normal_enabled = true
|
||||||
|
normal_scale = 0.4
|
||||||
|
normal_texture = ExtResource("2_k2yhv")
|
||||||
|
rim_enabled = true
|
||||||
|
rim = 0.25
|
||||||
|
clearcoat_roughness = 0.0
|
||||||
|
ao_enabled = true
|
||||||
|
ao_light_affect = 1.0
|
||||||
|
ao_texture_channel = 3
|
||||||
|
refraction_enabled = true
|
||||||
|
refraction_scale = -0.1
|
||||||
17
demo/assets/materials/M_rock23_black_tp.tres
Normal file
17
demo/assets/materials/M_rock23_black_tp.tres
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
[gd_resource type="StandardMaterial3D" load_steps=3 format=3 uid="uid://d0hyi5n6ng25w"]
|
||||||
|
|
||||||
|
[ext_resource type="Texture2D" uid="uid://c88j3oj0lf6om" path="res://demo/assets/textures/rock023_alb_ht.png" id="1_1js8i"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://c307hdmos4gtm" path="res://demo/assets/textures/rock023_nrm_rgh.png" id="2_snl3x"]
|
||||||
|
|
||||||
|
[resource]
|
||||||
|
albedo_color = Color(0.501406, 0.501407, 0.501406, 1)
|
||||||
|
albedo_texture = ExtResource("1_1js8i")
|
||||||
|
roughness = 0.95
|
||||||
|
roughness_texture = ExtResource("2_snl3x")
|
||||||
|
roughness_texture_channel = 3
|
||||||
|
normal_enabled = true
|
||||||
|
normal_texture = ExtResource("2_snl3x")
|
||||||
|
uv1_scale = Vector3(0.4, 0.4, 0.4)
|
||||||
|
uv1_triplanar = true
|
||||||
|
uv1_world_triplanar = true
|
||||||
|
texture_filter = 5
|
||||||
16
demo/assets/materials/M_rock23_tp.tres
Normal file
16
demo/assets/materials/M_rock23_tp.tres
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
[gd_resource type="StandardMaterial3D" load_steps=3 format=3 uid="uid://nbbdrx8vma80"]
|
||||||
|
|
||||||
|
[ext_resource type="Texture2D" uid="uid://c88j3oj0lf6om" path="res://demo/assets/textures/rock023_alb_ht.png" id="1_mek0c"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://c307hdmos4gtm" path="res://demo/assets/textures/rock023_nrm_rgh.png" id="2_pp13c"]
|
||||||
|
|
||||||
|
[resource]
|
||||||
|
vertex_color_use_as_albedo = true
|
||||||
|
albedo_color = Color(0.83, 0.83, 0.83, 1)
|
||||||
|
albedo_texture = ExtResource("1_mek0c")
|
||||||
|
normal_enabled = true
|
||||||
|
normal_texture = ExtResource("2_pp13c")
|
||||||
|
uv1_scale = Vector3(0.02, 0.02, 0.02)
|
||||||
|
uv1_triplanar = true
|
||||||
|
uv1_triplanar_sharpness = 6.06286
|
||||||
|
uv1_world_triplanar = true
|
||||||
|
texture_filter = 5
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
[gd_resource type="StandardMaterial3D" load_steps=3 format=3 uid="uid://dlmtxs0c66hj3"]
|
|
||||||
|
|
||||||
[ext_resource type="Texture2D" uid="uid://br7bfxcxh60df" path="res://demo/assets/textures/rock030_alb_ht.png" id="1_j5sdo"]
|
|
||||||
[ext_resource type="Texture2D" uid="uid://cr72kjil43vgj" path="res://demo/assets/textures/rock030_nrm_rgh.png" id="2_yyurh"]
|
|
||||||
|
|
||||||
[resource]
|
|
||||||
vertex_color_use_as_albedo = true
|
|
||||||
albedo_color = Color(1.351, 1.321, 1.27, 1)
|
|
||||||
albedo_texture = ExtResource("1_j5sdo")
|
|
||||||
normal_enabled = true
|
|
||||||
normal_texture = ExtResource("2_yyurh")
|
|
||||||
uv1_scale = Vector3(4, 4, 4)
|
|
||||||
uv1_triplanar_sharpness = 6.06286
|
|
||||||
texture_filter = 5
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
[gd_resource type="StandardMaterial3D" load_steps=3 format=3 uid="uid://dkvsksxpkl1vj"]
|
|
||||||
|
|
||||||
[ext_resource type="Texture2D" uid="uid://br7bfxcxh60df" path="res://demo/assets/textures/rock030_alb_ht.png" id="1_j5sdo"]
|
|
||||||
[ext_resource type="Texture2D" uid="uid://cr72kjil43vgj" path="res://demo/assets/textures/rock030_nrm_rgh.png" id="2_yyurh"]
|
|
||||||
|
|
||||||
[resource]
|
|
||||||
vertex_color_use_as_albedo = true
|
|
||||||
albedo_color = Color(1.351, 1.321, 1.27, 1)
|
|
||||||
albedo_texture = ExtResource("1_j5sdo")
|
|
||||||
normal_enabled = true
|
|
||||||
normal_texture = ExtResource("2_yyurh")
|
|
||||||
uv1_scale = Vector3(0.04, 0.04, 0.04)
|
|
||||||
uv1_triplanar = true
|
|
||||||
uv1_triplanar_sharpness = 6.06286
|
|
||||||
texture_filter = 5
|
|
||||||
56
demo/assets/models/LODExample.tscn
Normal file
56
demo/assets/models/LODExample.tscn
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
[gd_scene load_steps=9 format=3 uid="uid://bn5nf4esciwex"]
|
||||||
|
|
||||||
|
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_788j8"]
|
||||||
|
albedo_color = Color(1, 0, 0, 1)
|
||||||
|
|
||||||
|
[sub_resource type="SphereMesh" id="SphereMesh_u4ac2"]
|
||||||
|
material = SubResource("StandardMaterial3D_788j8")
|
||||||
|
|
||||||
|
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_3i52q"]
|
||||||
|
albedo_color = Color(1, 0.540167, 0.11, 1)
|
||||||
|
|
||||||
|
[sub_resource type="BoxMesh" id="BoxMesh_xyuxq"]
|
||||||
|
material = SubResource("StandardMaterial3D_3i52q")
|
||||||
|
size = Vector3(0.85, 0.85, 0.85)
|
||||||
|
|
||||||
|
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_p0ha4"]
|
||||||
|
albedo_color = Color(0.48, 1, 0.497333, 1)
|
||||||
|
|
||||||
|
[sub_resource type="PrismMesh" id="PrismMesh_xnm4h"]
|
||||||
|
material = SubResource("StandardMaterial3D_p0ha4")
|
||||||
|
size = Vector3(1, 1, 0.855)
|
||||||
|
|
||||||
|
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_4koei"]
|
||||||
|
albedo_color = Color(0.45, 0.596667, 1, 1)
|
||||||
|
|
||||||
|
[sub_resource type="CylinderMesh" id="CylinderMesh_dp7xj"]
|
||||||
|
material = SubResource("StandardMaterial3D_4koei")
|
||||||
|
top_radius = 0.0
|
||||||
|
bottom_radius = 0.34
|
||||||
|
height = 1.54
|
||||||
|
radial_segments = 4
|
||||||
|
|
||||||
|
[node name="TestMultimesh" type="Node3D"]
|
||||||
|
|
||||||
|
[node name="Node3D" type="Node3D" parent="."]
|
||||||
|
|
||||||
|
[node name="MeshInstance3D1" type="MeshInstance3D" parent="Node3D"]
|
||||||
|
mesh = SubResource("SphereMesh_u4ac2")
|
||||||
|
skeleton = NodePath("../..")
|
||||||
|
|
||||||
|
[node name="MeshInstance3D2" type="MeshInstance3D" parent="Node3D"]
|
||||||
|
visible = false
|
||||||
|
mesh = SubResource("BoxMesh_xyuxq")
|
||||||
|
skeleton = NodePath("../..")
|
||||||
|
|
||||||
|
[node name="MeshInstance3D3" type="MeshInstance3D" parent="Node3D"]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.118133, 0)
|
||||||
|
visible = false
|
||||||
|
mesh = SubResource("PrismMesh_xnm4h")
|
||||||
|
skeleton = NodePath("../..")
|
||||||
|
|
||||||
|
[node name="MeshInstance3D4" type="MeshInstance3D" parent="Node3D"]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.246009, 0)
|
||||||
|
visible = false
|
||||||
|
mesh = SubResource("CylinderMesh_dp7xj")
|
||||||
|
skeleton = NodePath("../..")
|
||||||
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user