built more assets and started playing with foliage painting
This commit is contained in:
41
addons/terrain_3d/extras/import_sgt.gd
Normal file
41
addons/terrain_3d/extras/import_sgt.gd
Normal file
@@ -0,0 +1,41 @@
|
||||
## Import From SimpleGrassTextured
|
||||
#
|
||||
# This script demonstrates how to import transforms from SimpleGrassTextured. To use it:
|
||||
#
|
||||
# 1. Setup the mesh asset you wish to use in the asset dock.
|
||||
# 1. Select your Terrain3D node.
|
||||
# 1. In the inspector, click Script (very bottom) and Quick Load import_sgt.gd.
|
||||
# 1. At the very top, assign your SimpleGrassTextured node.
|
||||
# 1. Input the desired mesh asset ID.
|
||||
# 1. Click import. The output window and console will report when finished.
|
||||
# 1. Clear the script from your Terrain3D node, and save your scene.
|
||||
#
|
||||
# The instance transforms are now stored in your region files.
|
||||
#
|
||||
# Use clear_instances to erase all instances that match the assign_mesh_id.
|
||||
#
|
||||
# The add_transforms function (called by add_multimesh) applies the height_offset specified in the
|
||||
# Terrain3DMeshAsset.
|
||||
# Once the transforms are imported, you can reassign any mesh you like into this mesh slot.
|
||||
|
||||
@tool
|
||||
extends Terrain3D
|
||||
|
||||
@export var simple_grass_textured: MultiMeshInstance3D
|
||||
@export var assign_mesh_id: int
|
||||
@export var import: bool = false : set = import_sgt
|
||||
@export var clear_instances: bool = false : set = clear_multimeshes
|
||||
|
||||
|
||||
func clear_multimeshes(value: bool) -> void:
|
||||
get_instancer().clear_by_mesh(assign_mesh_id)
|
||||
|
||||
|
||||
func import_sgt(value: bool) -> void:
|
||||
var sgt_mm: MultiMesh = simple_grass_textured.multimesh
|
||||
var global_xform: Transform3D = simple_grass_textured.global_transform
|
||||
print("Starting to import %d instances from SimpleGrassTextured using mesh id %d" % [ sgt_mm.instance_count, assign_mesh_id])
|
||||
var time: int = Time.get_ticks_msec()
|
||||
get_instancer().add_multimesh(assign_mesh_id, sgt_mm, simple_grass_textured.global_transform)
|
||||
print("Import complete in %.2f seconds" % [ float(Time.get_ticks_msec() - time)/1000. ])
|
||||
|
||||
164
addons/terrain_3d/extras/minimum.gdshader
Normal file
164
addons/terrain_3d/extras/minimum.gdshader
Normal file
@@ -0,0 +1,164 @@
|
||||
// This shader is the minimum needed to allow the terrain to function, without any texturing.
|
||||
|
||||
shader_type spatial;
|
||||
render_mode blend_mix,depth_draw_opaque,cull_back,diffuse_burley,specular_schlick_ggx,skip_vertex_transform;
|
||||
|
||||
// Private uniforms
|
||||
uniform float _region_size = 1024.0;
|
||||
uniform float _region_texel_size = 0.0009765625; // = 1/1024
|
||||
uniform float _vertex_spacing = 1.0;
|
||||
uniform float _vertex_density = 1.0; // = 1/_vertex_spacing
|
||||
uniform int _region_map_size = 32;
|
||||
uniform int _region_map[1024];
|
||||
uniform vec2 _region_locations[1024];
|
||||
uniform sampler2DArray _height_maps : repeat_disable;
|
||||
uniform usampler2DArray _control_maps : repeat_disable;
|
||||
uniform sampler2DArray _color_maps : source_color, filter_linear_mipmap_anisotropic, repeat_disable;
|
||||
uniform sampler2DArray _texture_array_albedo : source_color, filter_linear_mipmap_anisotropic, repeat_enable;
|
||||
uniform sampler2DArray _texture_array_normal : hint_normal, filter_linear_mipmap_anisotropic, repeat_enable;
|
||||
uniform sampler2D noise_texture : source_color, filter_linear_mipmap_anisotropic, repeat_enable;
|
||||
|
||||
uniform float _texture_uv_scale_array[32];
|
||||
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
|
||||
varying flat vec3 v_vertex; // World coordinate vertex location
|
||||
varying flat vec3 v_camera_pos;
|
||||
varying float v_vertex_xz_dist;
|
||||
varying flat ivec3 v_region;
|
||||
varying flat vec2 v_uv_offset;
|
||||
varying flat vec2 v_uv2_offset;
|
||||
varying vec3 v_normal;
|
||||
varying float v_region_border_mask;
|
||||
|
||||
////////////////////////
|
||||
// Vertex
|
||||
////////////////////////
|
||||
|
||||
// Takes in UV world space coordinates, returns ivec3 with:
|
||||
// XY: (0 to _region_size) coordinates within a region
|
||||
// Z: layer index used for texturearrays, -1 if not in a region
|
||||
ivec3 get_region_uv(const vec2 uv) {
|
||||
ivec2 pos = ivec2(floor(uv * _region_texel_size)) + (_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 ivec3(ivec2(mod(uv,_region_size)), layer_index);
|
||||
}
|
||||
|
||||
// Takes in UV2 region space 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_region_uv2(const vec2 uv2) {
|
||||
// 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;
|
||||
}
|
||||
|
||||
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
|
||||
v_vertex = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;
|
||||
|
||||
// Camera distance to vertex on flat plane
|
||||
v_vertex_xz_dist = length(v_vertex.xz - v_camera_pos.xz);
|
||||
|
||||
// UV coordinates in world space. Values are 0 to _region_size within regions
|
||||
UV = round(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
|
||||
v_region = get_region_uv(UV);
|
||||
uint control = texelFetch(_control_maps, v_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 && (get_region_uv(UV - _region_texel_size) & v_region).z < 0))) {
|
||||
VERTEX.x = 0. / 0.;
|
||||
} else {
|
||||
// Set final vertex height & calculate vertex normals. 3 lookups.
|
||||
VERTEX.y = get_height(UV2);
|
||||
v_vertex.y = VERTEX.y;
|
||||
v_normal = vec3(
|
||||
v_vertex.y - get_height(UV2 + vec2(_region_texel_size, 0)),
|
||||
_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
|
||||
VERTEX = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;
|
||||
VERTEX = (VIEW_MATRIX * vec4(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
|
||||
////////////////////////
|
||||
|
||||
// 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() {
|
||||
// Recover UVs
|
||||
vec2 uv = UV + v_uv_offset;
|
||||
vec2 uv2 = UV2 + v_uv2_offset;
|
||||
|
||||
// Calculate Terrain Normals. 4 lookups
|
||||
vec3 w_tangent, w_binormal;
|
||||
vec3 w_normal = get_normal(uv2, w_tangent, w_binormal);
|
||||
NORMAL = mat3(VIEW_MATRIX) * w_normal;
|
||||
TANGENT = mat3(VIEW_MATRIX) * w_tangent;
|
||||
BINORMAL = mat3(VIEW_MATRIX) * w_binormal;
|
||||
|
||||
// Apply PBR
|
||||
ALBEDO=vec3(.2);
|
||||
}
|
||||
90
addons/terrain_3d/extras/project_on_terrain3d.gd
Normal file
90
addons/terrain_3d/extras/project_on_terrain3d.gd
Normal file
@@ -0,0 +1,90 @@
|
||||
# This script is an addon for HungryProton's Scatter https://github.com/HungryProton/scatter
|
||||
# It provides a `Project on Terrain3D` modifier, which allows Scatter
|
||||
# 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
|
||||
# Then uncomment everything below (select, press CTRL+K)
|
||||
# In the editor, add this modifier, then set your Terrain3D node
|
||||
|
||||
#@tool
|
||||
#extends "base_modifier.gd"
|
||||
#
|
||||
#
|
||||
#signal projection_completed
|
||||
#
|
||||
#
|
||||
#@export var terrain_node : NodePath
|
||||
#@export var align_with_collision_normal := false
|
||||
#
|
||||
#var _terrain: Terrain3D
|
||||
#
|
||||
#
|
||||
#func _init() -> void:
|
||||
#display_name = "Project On Terrain3D"
|
||||
#category = "Edit"
|
||||
#can_restrict_height = false
|
||||
#global_reference_frame_available = true
|
||||
#local_reference_frame_available = true
|
||||
#individual_instances_reference_frame_available = true
|
||||
#use_global_space_by_default()
|
||||
#
|
||||
#documentation.add_paragraph(
|
||||
#"This is a duplicate of `Project on Colliders` that queries the terrain system
|
||||
#for height and sets the transform height appropriately.
|
||||
#
|
||||
#This modifier must have terrain_node set to a Terrain3D node.")
|
||||
#
|
||||
#var p := documentation.add_parameter("Terrain Node")
|
||||
#p.set_type("NodePath")
|
||||
#p.set_description("Set your Terrain3D node.")
|
||||
#
|
||||
#p = documentation.add_parameter("Align with collision normal")
|
||||
#p.set_type("bool")
|
||||
#p.set_description(
|
||||
#"Rotate the transform to align it with the collision normal in case
|
||||
#the ray cast hit a collider.")
|
||||
#
|
||||
#
|
||||
#func _process_transforms(transforms, domain, _seed) -> void:
|
||||
#if transforms.is_empty():
|
||||
#return
|
||||
#
|
||||
#if terrain_node:
|
||||
#_terrain = domain.get_root().get_node_or_null(terrain_node)
|
||||
#
|
||||
#if not _terrain:
|
||||
#warning += """No Terrain3D node found"""
|
||||
#return
|
||||
#
|
||||
#if not _terrain.data:
|
||||
#warning += """Terrain3DData is not initialized"""
|
||||
#return
|
||||
#
|
||||
## Get global transform
|
||||
#var gt: Transform3D = domain.get_global_transform()
|
||||
#var gt_inverse := gt.affine_inverse()
|
||||
#for i in transforms.list.size():
|
||||
#var location: Vector3 = (gt * transforms.list[i]).origin
|
||||
#var height: float = _terrain.data.get_height(location)
|
||||
#var normal: Vector3 = _terrain.data.get_normal(location)
|
||||
#
|
||||
#if align_with_collision_normal and not is_nan(normal.x):
|
||||
#transforms.list[i].basis.y = normal
|
||||
#transforms.list[i].basis.x = -transforms.list[i].basis.z.cross(normal)
|
||||
#transforms.list[i].basis = transforms.list[i].basis.orthonormalized()
|
||||
#
|
||||
#transforms.list[i].origin.y = gt.origin.y if is_nan(height) else height - gt.origin.y
|
||||
#
|
||||
#if transforms.is_empty():
|
||||
#warning += """Every point has been removed. Possible reasons include: \n
|
||||
#+ No collider is close enough to the shapes.
|
||||
#+ Ray length is too short.
|
||||
#+ Ray direction is incorrect.
|
||||
#+ Collision mask is not set properly.
|
||||
#+ Max slope is too low.
|
||||
#"""
|
||||
Reference in New Issue
Block a user