Compare commits

..

9 Commits

  1. BIN
      map.png
  2. 35
      map.png.import
  3. 12
      project.godot
  4. 9
      ui/map/cursor/Cursor.gd
  5. 117
      ui/map/map.gd
  6. 6
      ui/map/map.tscn
  7. 59
      utils/Global.gd
  8. 6
      utils/camera/CameraController.gd
  9. 2
      utils/camera/CameraInput.gd
  10. 51
      utils/map/map.gd
  11. 67
      utils/terrain/Terrain.gd
  12. 11
      utils/world_generation/WorldGeneration.gd
  13. 113
      world/Chunk.gd
  14. BIN
      world/Default OBJ.material
  15. 29
      world/MultiMeshInstance.gd
  16. 120
      world/World3d.gd
  17. 32
      world/game.tscn
  18. 12
      world/grass.mtl
  19. 32
      world/grass.obj
  20. 20
      world/grass.obj.import
  21. 29
      world/grass_triangle.obj
  22. 20
      world/grass_triangle.obj.import
  23. BIN
      world/materials/colors.png
  24. 77
      world/materials/wind_grass.shader
  25. 40
      world/materials/wind_grass.tres

Binary file not shown.

Before

Width:  |  Height:  |  Size: 448 KiB

@ -1,35 +0,0 @@
[remap]
importer="texture"
type="StreamTexture"
path="res://.import/map.png-9eea34967fae34f4388f4a32a16da936.stex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://map.png"
dest_files=[ "res://.import/map.png-9eea34967fae34f4388f4a32a16da936.stex" ]
[params]
compress/mode=0
compress/lossy_quality=0.7
compress/hdr_mode=0
compress/bptc_ldr=0
compress/normal_map=0
flags/repeat=0
flags/filter=true
flags/mipmaps=false
flags/anisotropic=false
flags/srgb=2
process/fix_alpha_border=true
process/premult_alpha=false
process/HDR_as_SRGB=false
process/invert_color=false
process/normal_map_invert_y=false
stream=false
size_limit=0
detect_3d=true
svg/scale=1.0

@ -19,6 +19,11 @@ _global_script_classes=[ {
"language": "GDScript",
"path": "res://utils/camera/outline.gd"
}, {
"base": "Spatial",
"class": "Chunk",
"language": "GDScript",
"path": "res://world/Chunk.gd"
}, {
"base": "Reference",
"class": "Delaunator",
"language": "GDScript",
@ -30,11 +35,6 @@ _global_script_classes=[ {
"path": "res://utils/loading/LoadingHelper.gd"
}, {
"base": "Reference",
"class": "Map",
"language": "GDScript",
"path": "res://utils/map/map.gd"
}, {
"base": "Reference",
"class": "PoissonDiscSampling",
"language": "GDScript",
"path": "res://addons/PoissonDiscSampling/PoissonDiscSampling.gd"
@ -57,9 +57,9 @@ _global_script_classes=[ {
_global_script_class_icons={
"CameraController": "",
"CameraOutline": "",
"Chunk": "",
"Delaunator": "",
"LoadingHelper": "",
"Map": "",
"PoissonDiscSampling": "",
"Terrain": "",
"TerrainMesh": "",

@ -3,9 +3,6 @@ extends Sprite
func _on_Camera_camera_moved(new_location):
var map_x = new_location.x
var map_y = new_location.z
position.x = map_x / 4.0
position.y = map_y / 4.0
func _ready():
scale.x = 1
scale.y = 1
position.x = map_x
position.y = map_y
pass # Replace with function body.

@ -1,18 +1,113 @@
extends TextureRect
extends Node2D
signal map_clicked
func _ready():
var file_name = 'user://terrain/%s/map.png' % (Global.terrain_name)
var image = Image.new()
var err = image.load(file_name)
if err != OK:
print('Image load failed : %s' % (file_name))
texture = ImageTexture.new()
texture.create_from_image(image, Image.FORMAT_RGBA8)
func heightmap():
draw_rect(Rect2(Vector2(0, 0), Vector2(2048, 2048)), Color("#0e88bd"))
var coastline = PoolVector2Array()
for center in Global.terrain.get_centers():
if not center.get_data("ocean"):
var colors = Gradient.new()
colors.add_point(0.999, Color("#9e0142")) # red
colors.add_point(0.5, Color("#dc865d")) # orange
colors.add_point(0.25, Color("#fbf8b0")) # yellow
colors.add_point(0.0, Color("#89cfa5")) # green
colors.add_point(-0.999, Color("#5e4fa2")) # blue
var color = colors.interpolate(min(center.get_elevation() + 0.001, 0.999))
var moisture = center.get_data("moisture")
if moisture:
color = colors.interpolate(max(min(moisture + 0.001, 0.999), 0.001))
# color = Color.green
if center.get_data("ocean"):
# var factor = pow((center.get_elevation()+1.001), 10) / 5.0
color = Color("#5e4fa2")
# if center.get_data("snow"):
# color = Color.white
# if center.get_data("coast"):
# color = Color.black
if center.polygon().size() > 2:
draw_polygon(center.polygon(), PoolColorArray([color]))
if center.get_data("coast"):
for border in center.borders():
if (border.end_center().get_data("ocean")):
coastline.append(border.line()[0])
coastline.append(border.line()[1])
# for edge in Global.terrain.get_edges():
# if edge.get_data("coast"):
# if edge.get_data("river"):
# draw_line(edge.line()[0], edge.line()[1], Color.blue, 5.0)
draw_multiline(coastline, Color.black)
func draw_triangles_edges(color=Color("#000000")):
for line in Global.terrain.get_edges_as_line():
draw_line(line[0], line[1], color)
func draw_voronoi_edges(color=Color("#000000")):
for line in Global.terrain.get_voronoi_edges_as_line():
draw_line(line[0], line[1], color)
func draw_voronoi_cells_old():
var seen = []
for edge_idx in Global.terrain.edges():
var triangles = []
var vertices = []
var p = Global.terrain._triangles[Global.terrain.next_half_edge(edge_idx)]
if not seen.has(p):
seen.append(p)
var edges = Global.terrain.edges_around_point(edge_idx)
for edge_around_idx in edges:
triangles.append(Global.terrain.triangle_of_edge(edge_around_idx))
for triangle in triangles:
vertices.append(Global.terrain.triangle_center(triangle))
if triangles.size() > 2:
var color = Color(randf(), randf(), randf(), 1)
var voronoi_cell = PoolVector2Array()
for vertice in vertices:
voronoi_cell.append(Vector2(vertice.x, vertice.z))
draw_polygon(voronoi_cell, PoolColorArray([color]))
func draw_voronoi_cells():
for polygon in Global.terrain.get_voronoi_cells_as_polygon():
var color = Color(randf(), randf(), randf(), 1)
if polygon.size() > 2:
draw_polygon(polygon, PoolColorArray([color]))
func draw_voronoi_cells_convex_hull():
for point_idx in Global.terrain.points():
var triangles = []
var vertices = []
var incoming = Global.terrain._points_to_half_edges.get(point_idx)
if incoming == null:
triangles.append(0)
else:
var edges = Global.terrain.edges_around_point(incoming)
for edge_idx in edges:
triangles.append(Global.terrain.triangle_of_edge(edge_idx))
for triangle_idx in triangles:
vertices.append(Global.terrain.triangle_center(triangle_idx))
if triangles.size() > 2:
var color = Color(randf(), randf(), randf(), 1)
var voronoi_cell = PoolVector2Array()
for vertice in vertices:
voronoi_cell.append(Vector2(vertice[0], vertice[1]))
draw_polygon(voronoi_cell, PoolColorArray([color]))
func _draw():
heightmap()
# draw_voronoi_cells()
# draw_triangles_edges()
# draw_voronoi_cells_convex_hull()
# draw_voronoi_edges(Color("#ff0000"))
func _process(_delta):
if Input.is_action_pressed("alt_command"):
var new_position = get_viewport().get_mouse_position()
if new_position.x <= 512 and new_position.y <= 512:
var new_position = get_viewport().get_mouse_position() / scale
if new_position.x <= 2000 and new_position.y <= 2000:
emit_signal("map_clicked", new_position)

@ -4,11 +4,7 @@
[ext_resource path="res://ui/map/cursor/cursor.png" type="Texture" id=2]
[ext_resource path="res://ui/map/cursor/Cursor.gd" type="Script" id=3]
[node name="Map" type="TextureRect"]
anchor_right = 0.5
anchor_bottom = 0.852
margin_right = 3.0
margin_bottom = 0.799988
[node name="Map" type="Node2D"]
script = ExtResource( 1 )
[node name="Cursor" type="Sprite" parent="."]

@ -4,7 +4,6 @@ var debug = true
var terrain_name = ""
var terrain_mesh: Mesh
var terrain = Terrain.new()
var map = Map.new(terrain)
# var loading = LoadingHelper.new()
var loadings = {}
var materials
@ -15,64 +14,6 @@ func _ready():
file.open("res://world/materials/materials.json", File.READ)
materials = JSON.parse(file.get_as_text()).result
file.close()
func polygon_area(polygon):
var a = 0.0
var b = 0.0
var next = 0
var size = polygon.size()
if(Geometry.is_polygon_clockwise(polygon)):
for idx in range(size-1, -1, -1):
next = idx - 1
if(next == 0):
next = size - 1
a += polygon[idx].x * polygon[next].y
b += polygon[idx].y * polygon[next].x
else:
for idx in size:
next = idx + 1
if(next == size):
next = 0
a += polygon[idx].x * polygon[next].y
b += polygon[idx].y * polygon[next].x
return((a - b) / 2.0)
func polygon_bounding_box(polygon):
var size = polygon.size()
var min_x = 99999999
var max_x = -99999999
var min_y = 99999999
var max_y = -99999999
var polygon_xmin = 0
var polygon_xmax = 1
var polygon_ymin = 0
var polygon_ymax = 1
for idx in size:
polygon_xmin = int(polygon[idx].x)
polygon_xmax = polygon_xmin + 1
polygon_ymin = int(polygon[idx].y)
polygon_ymax = polygon_ymin + 1
if(polygon_xmin < min_x):
min_x = polygon_xmin
if(polygon_xmax > max_x):
max_x = polygon_xmax
if(polygon_ymin < min_y):
min_y = polygon_ymin
if(polygon_ymax > max_y):
max_y = polygon_ymax
return(Rect2(min_x,min_y,max_x - min_x, max_y - min_y))
func pixel_area(voronoi, pixel):
var polygons = Geometry.intersect_polygons_2d(voronoi, pixel)
var number = polygons.size()
var area = 0.0
if(number == 0):
return(0.0)
if(number > 1):
print_debug("Number of polygons : %d" % (number))
for idx in number:
area += polygon_area(polygons[idx])
return(area)
# Debuging messages
func print_debug(message):

@ -50,7 +50,7 @@ func change_action(action):
CAMERA_ACTIONS.ROTATING_VIEW:
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
func _process(_delta):
func _process(delta):
match(current_action):
CAMERA_ACTIONS.MOVING:
@ -121,8 +121,8 @@ func rotate_view(axis : Vector2):
rotation.y = yaw
func teleport(position):
global_transform.origin.x = position.x * 4.0
global_transform.origin.z = position.y * 4.0
global_transform.origin.x = position.x
global_transform.origin.z = position.y
var y_offset = 0
var space_state = get_world().direct_space_state

@ -139,7 +139,7 @@ func _input(event):
mouse_wheel += direction * get_process_delta_time() * 1000
###################PC
func _process(_delta):
func _process(delta):
#PC######
match(current_action):

@ -1,51 +0,0 @@
extends Reference
# Build terrain from delaunay graph
class_name Map
var image
var terrain
# Called when the node enters the scene tree for the first time.
func _init(a_terrain):
self.terrain = a_terrain
a_terrain.set_data("map",self)
func gen_map():
Global.loadings["world_creation"].new_phase("Generation de la carte...", terrain._points.size())
image = Image.new()
image.create(terrain._width,terrain._height,false,Image.FORMAT_RGBA8)
image.lock()
image.fill(Color('#5aa6ca'))
image.unlock()
var file_name = "user://terrain/%s/map.png" % (terrain.get_name())
for center in terrain.get_centers():
if not center.get_data("water"):
var voronoi = center.get_data("voronoi")
var voronoi_bounding_box = center.get_data("voronoi_bounding_box")
# print_debug("Creat voronoi image")
var voronoi_image = Image.new()
voronoi_image.create(int(voronoi_bounding_box.size.x), int(voronoi_bounding_box.size.y),false,Image.FORMAT_RGBA8)
voronoi_image.lock()
for x in int(voronoi_bounding_box.size.x):
for y in int(voronoi_bounding_box.size.y):
var pixel = []
pixel.append(Vector2(voronoi_bounding_box.position.x + x, voronoi_bounding_box.position.y + y))
pixel.append(Vector2(voronoi_bounding_box.position.x + x + 1, voronoi_bounding_box.position.y + y))
pixel.append(Vector2(voronoi_bounding_box.position.x + x + 1, voronoi_bounding_box.position.y + y + 1))
pixel.append(Vector2(voronoi_bounding_box.position.x + x, voronoi_bounding_box.position.y + y + 1))
var alpha = Global.pixel_area(voronoi, pixel)
# print_debug("Alpha : %f" % (alpha))
var color
if center.get_data("coast"):
color = Color(0.708, 0.646, 0.138, alpha)
else:
color = Color(0.253, 0.621, 0.229, alpha)
voronoi_image.set_pixel(x,y,color)
image.lock()
image.blend_rect(voronoi_image,Rect2(0.0,0.0,voronoi_bounding_box.size.x,voronoi_bounding_box.size.y),voronoi_bounding_box.position)
image.unlock()
voronoi_image.unlock()
Global.loadings["world_creation"].increment_step()
image.save_png(file_name)

@ -106,8 +106,7 @@ class VoronoiCenter:
func corners():
var list_corners = []
var a_point = to_point()
for triangle in a_point.triangles_around():
for triangle in to_point().triangles_around():
var corner = VoronoiCorner.new(triangle)
list_corners.append(corner)
return list_corners
@ -292,6 +291,7 @@ class Triangle:
)
set_data("center2d", circumcenter)
return circumcenter
return circumcenter
# return (points[0].point2d() + points[1].point2d() + points[2].point2d()) / 3.0
@ -314,6 +314,7 @@ class Triangle:
)
set_data("center3d", circumcenter)
return circumcenter
return circumcenter
# var center2d = center2d()
# return Vector3(center2d.x, )
@ -429,16 +430,15 @@ class Point:
func edges_around():
var list_edges = []
if _terrain._points_to_halfedges.has(_idx):
var incoming = _terrain._points_to_halfedges.get(_idx)
var incoming_edge = Edge.new(incoming, _terrain)
var outgoing_edge
while true:
list_edges.append(incoming_edge);
outgoing_edge = incoming_edge.next_half()
incoming_edge = Edge.new(_terrain._halfedges[outgoing_edge._idx], _terrain);
if not (incoming_edge._idx != -1 and incoming_edge._idx != incoming):
break
var incoming = _terrain._points_to_halfedges.get(_idx)
var incoming_edge = Edge.new(incoming, _terrain)
var outgoing_edge
while true:
list_edges.append(incoming_edge);
outgoing_edge = incoming_edge.next_half()
incoming_edge = Edge.new(_terrain._halfedges[outgoing_edge._idx], _terrain);
if not (incoming_edge._idx != -1 and incoming_edge._idx != incoming):
break
return list_edges
func points_around():
@ -579,7 +579,7 @@ var _path = ""
var _list = []
# Terrain constructor
func _init():
func _init(width:int=1600, height:int=800, spacing:int=30, create=false, name:String=""):
var directory = Directory.new()
var file = File.new()
var file_name = ""
@ -616,6 +616,18 @@ func _init():
directory_name = directory.get_next()
directory.list_dir_end()
# Create or Load Terrain
_path = "user://terrain/%s" % (name)
parameter_file_name = "%s/param.save" % (_path)
graph_file_name = "%s/graph.save" % (_path)
data_file_name = "%s/data.save" % (_path)
if directory.open(_path) == OK and file.file_exists(parameter_file_name) and file.file_exists(graph_file_name) and file.file_exists(data_file_name) and not create:
Global.print_debug("loading : %s ..." % (name))
load(name)
else:
if name:
create(width, height, spacing, name)
func create(width:int, height:int, spacing:int, name:String):
Global.print_debug("Creating : %s ..." % (name))
var delaunay: Delaunator
@ -631,6 +643,15 @@ func create(width:int, height:int, spacing:int, name:String):
_triangles = PoolIntArray(delaunay.triangles)
Global.loadings["world_creation"].new_phase("Initialisation du terrain...", get_points().size() + get_edges().size())
# Initialize find_point
_data["find_point"]=[]
_data["find_point"].resize(1024)
for idx in 1024:
_data["find_point"][idx]=[]
for point in get_points():
_data["find_point"][point.find_index()].append(point.get_index())
Global.loadings["world_creation"].increment_step()
# Initialize _points_to_halfedges
for edge in get_edges():
@ -639,7 +660,6 @@ func create(width:int, height:int, spacing:int, name:String):
_points_to_halfedges[endpoint] = edge.get_index()
Global.loadings["world_creation"].increment_step()
_create_find_point_table()
# Initialise _points_data
for point_idx in self.get_points().size():
_points_data.append({})
@ -657,22 +677,6 @@ func create(width:int, height:int, spacing:int, name:String):
_created = true
save()
# Create a table to find a point from vector2
func _create_find_point_table():
var find_point = []
# var corners = {}
# var edges = {}
find_point.resize(1024)
for idx in 1024:
find_point[idx]=[]
for point in get_points():
var point_index = point.get_index()
find_point[point.find_index()].append(point_index)
var center = point.to_center()
set_data("find_point",find_point)
# Create points on the terrain
func _create_points():
@ -694,9 +698,6 @@ func set_temp_data(key, value):
func get_data(key):
if _data.has(key):
return _data[key]
func get_name():
return _name
func get_temp_data(key):
if _temp_data.has(key):

@ -26,7 +26,7 @@ func _init():
Global.loadings["world_creation"].start(coeffs, "Chargement...", 100)
Global.terrain.load(Global.terrain_name)
else:
var coeffs = [0, 1, 2, 2, 2, 2, 2, 8, 8]
var coeffs = [0, 1, 2, 2, 2, 2, 2, 8]
Global.loadings["world_creation"].start(coeffs, "Start", 100)
Global.terrain.create(width,height,spacing,Global.terrain_name)
@ -36,7 +36,6 @@ func _init():
Global.terrain.save()
var terrain_mesh = TerrainMesh.new()
Global.terrain.set_temp_data("mesh", terrain_mesh.create_mesh())
Global.map.gen_map()
if Global.terrain.is_loaded():
var terrain_mesh = TerrainMesh.new()
@ -74,7 +73,6 @@ func init_data():
center.set_data("material", "stone")
if center.get_data("coast"):
center.set_data("material", "sand")
if (
not center.get_data("coast")
and not center.get_data("mountain")
@ -82,13 +80,6 @@ func init_data():
and center.get_data("moisture") > 0.3
):
center.set_data("forest", true)
if not center.get_data("water"):
var voronoi = center.polygon()
center.set_data("voronoi",voronoi)
var voronoi_bounding_box = Global.polygon_bounding_box(voronoi)
center.set_data("voronoi_bounding_box",voronoi_bounding_box)
Global.loadings["world_creation"].increment_step()

@ -0,0 +1,113 @@
extends Spatial
class_name Chunk
var noise
var should_remove = true
var x
var z
var empty = true
func _init(x, z):
self.x = x
self.z = z
func _ready():
# generate_chunk()
generate_grass()
pass
func generate_grass():
var poisson_disc_sampling: PoissonDiscSampling = PoissonDiscSampling.new()
var rng := RandomNumberGenerator.new()
rng.randomize()
var coords = []
for center in Global.terrain.get_chunk(Vector2(x, z)):
if (
not center.get_data("mountain")
and not center.get_data("water")
and not center.get_data("coast")
):
var points = poisson_disc_sampling.generate_points(0.1, center.polygon(), 2)
var points3d = []
for point in points:
points3d.append(Vector3(point.x, center.get_elevation() * 120, point.y))
coords += points3d
# var multimesh = MultiMesh.new()
var multimesh = MultiMesh.new()
multimesh.mesh = load("res://world/grass.obj")
multimesh.mesh.surface_set_material(0, load("res://world/materials/wind_grass.tres"))
multimesh.transform_format = MultiMesh.TRANSFORM_3D
multimesh.instance_count = coords.size()
for instance_index in multimesh.instance_count:
var transform := Transform().rotated(Vector3.UP, rng.randf_range(-PI / 2, PI / 2))
transform.origin = Vector3(coords[instance_index].x, coords[instance_index].y, coords[instance_index].z)
# transform.scaled(Vector3())
multimesh.set_instance_transform(instance_index, transform)
var multimesh_instance = MultiMeshInstance.new()
multimesh_instance.multimesh = multimesh
add_child(multimesh_instance)
pass
func generate_chunk():
var file = File.new()
file.open("res://world/materials/materials.json", File.READ)
var materials = JSON.parse(file.get_as_text()).result
var st = SurfaceTool.new()
st.begin(Mesh.PRIMITIVE_TRIANGLES)
var factor = Vector3(1, 120, 1)
# print(x)
# print(z)
for center in Global.terrain.get_chunk(Vector2(x, z)):
# print(center.get_data("water"))
if not center.get_data("water"):
empty = false
# print(center.get_data("material"))
var material_id = materials[center.get_data("material")]
var top_uv = Vector2(0, float(material_id) / (materials.size()-1))
var border_uv = Vector2(1, float(material_id) / (materials.size()-1))
for edge in center.borders():
if edge.end_center().get_elevation() < edge.start_center().get_elevation():
var top = edge.start_center().get_elevation()
# if edge.start_center().get_data("ocean"):
# top = -1.0
var bottom = edge.end_center().get_elevation()
if edge.end_center().get_data("ocean"):
bottom = 0.0
st.add_uv(border_uv)
st.add_vertex(Vector3(edge.start_corner().point3d().x, bottom, edge.start_corner().point3d().z) * factor)
st.add_vertex(Vector3(edge.end_corner().point3d().x, top, edge.end_corner().point3d().z) * factor)
st.add_vertex(Vector3(edge.start_corner().point3d().x, top, edge.start_corner().point3d().z) * factor)
st.add_vertex(Vector3(edge.start_corner().point3d().x, bottom, edge.start_corner().point3d().z) * factor)
st.add_vertex(Vector3(edge.end_corner().point3d().x, bottom, edge.end_corner().point3d().z) * factor)
st.add_vertex(Vector3(edge.end_corner().point3d().x, top, edge.end_corner().point3d().z) * factor)
for corner_count in center.corners().size():
var current_corner = center.corners()[corner_count]
var next_corner
if corner_count < center.corners().size() - 1:
next_corner = center.corners()[corner_count+1]
else:
next_corner = center.corners()[0]
st.add_uv(Vector2(top_uv))
st.add_vertex(Vector3(current_corner.point2d().x, center.get_elevation(), current_corner.point2d().y) * factor)
st.add_vertex(Vector3(next_corner.point2d().x, center.get_elevation(), next_corner.point2d().y) * factor)
st.add_vertex(Vector3(center.point2d().x, center.get_elevation(), center.point2d().y) * factor)
if not empty:
st.generate_normals()
st.index()
var mi = MeshInstance.new()
mi.mesh = st.commit()
var material = load("res://world/materials/world.material")
mi.set_surface_material(0, material)
mi.create_trimesh_collision()
mi.cast_shadow = GeometryInstance.SHADOW_CASTING_SETTING_ON
add_child(mi)

Binary file not shown.

@ -0,0 +1,29 @@
extends MultiMeshInstance
var extents = Vector2(10, 10)
func _ready():
return
var poisson_disc_sampling: PoissonDiscSampling = PoissonDiscSampling.new()
var rng := RandomNumberGenerator.new()
rng.randomize()
var coords = []
for center in Global.terrain.get_centers():
if (
not center.get_data("mountain")
and not center.get_data("water")
and not center.get_data("coast")
):
var points = poisson_disc_sampling.generate_points(2, center.polygon(), 2)
var points3d = []
for point in points:
points3d.append(Vector3(point.x, center.get_elevation() * 120, point.y))
coords += points3d
multimesh.instance_count = coords.size()
for instance_index in multimesh.instance_count:
var transform := Transform().rotated(Vector3.UP, rng.randf_range(-PI / 2, PI / 2))
transform.origin = Vector3(coords[instance_index].x, coords[instance_index].y, coords[instance_index].z)
# transform.scaled(Vector3())
multimesh.set_instance_transform(instance_index, transform)

@ -2,13 +2,14 @@ extends Spatial
var rng = RandomNumberGenerator.new()
var chunk_size = 32
var chunk_amount = 16
var chunk_amount = 1
var chunks = {}
var unready_chunks = {}
var thread
func _ready():
add_world()
thread = Thread.new()
add_trees()
func add_world():
@ -29,13 +30,110 @@ func add_trees():
var poisson_disc_sampling: PoissonDiscSampling = PoissonDiscSampling.new()
for center in Global.terrain.get_centers():
var num = rng.randi_range(0,100)
if center.get_data("forest") or num == 1:
var points2d = poisson_disc_sampling.generate_points(3, center.polygon(), 2)
for point in points2d:
var tree = treescene.instance()
var scaling = rng.randi_range(0.8, 1.2)
tree.scale = Vector3(scaling, scaling, scaling)
tree.rotate_y(rng.randi_range(0, 2*PI))
tree.translation = Vector3(point.x, center.get_elevation() * 120, point.y)
add_child(tree)
if not center.get_data("water"):
var num = rng.randi_range(0,100)
if center.get_data("forest") or num == 1:
var points2d = poisson_disc_sampling.generate_points(3, center.polygon(), 2)
for point in points2d:
var tree = treescene.instance()
var scaling = rng.randi_range(0.8, 1.2)
tree.scale = Vector3(scaling, scaling, scaling)
tree.rotate_y(rng.randi_range(0, 2*PI))
tree.translation = Vector3(point.x, center.get_elevation() * 120, point.y)
add_child(tree)
func add_chunk(x, z):
var key = str(x) + "," + str(z)
if chunks.has(key) or unready_chunks.has(key):
return
if not thread.is_active():
thread.start(self, "load_chunk", [thread, x, z])
unready_chunks[key] = 1
func load_chunk(array):
var thread = array[0]
var x = array[1]
var z = array[2]
# print(x)
# print(z)
var chunk = Chunk.new(x * chunk_size, z * chunk_size)
# chunk.translation = Vector3(x * chunk_size, 0, z * chunk_size)
call_deferred("load_done", chunk, thread)
func load_done(chunk, thread):
add_child(chunk)
var key = str(chunk.x / chunk_size) + "," + str(chunk.z / chunk_size)
chunks[key] = chunk
unready_chunks.erase(key)
thread.wait_to_finish()
func get_chunk(x, z):
var key = str(x) + "," + str(z)
if chunks.has(key):
return chunks.get(key)
return null
func _process(delta):
update_chunks()
clean_up_chunks()
reset_chunks()
func update_chunks():
var camera_translation = $CamBase/Camera.translation
var c_x = int(camera_translation.x) / chunk_size
var c_z = int(camera_translation.y) / chunk_size * -1
var stack = []
var used = []
stack.append(Vector2(c_x, c_z))
while stack.size():
var current_vector = stack.pop_back()
used.append(current_vector)
add_chunk(current_vector.x, current_vector.y)
var chunk = get_chunk(current_vector.x, current_vector.y)
if chunk != null:
chunk.should_remove = false
var neighbours = [
Vector2(current_vector.x, current_vector.y - 1), # n
Vector2(current_vector.x + 1, current_vector.y - 1), # n_e
Vector2(current_vector.x + 1, current_vector.y), # e
Vector2(current_vector.x + 1, current_vector.y + 1), # s_e
Vector2(current_vector.x, current_vector.y + 1), # s
Vector2(current_vector.x - 1, current_vector.y + 1), # s_w
Vector2(current_vector.x - 1, current_vector.y), # w
Vector2(current_vector.x - 1, current_vector.y - 1) # n_w
]
for neighbour in neighbours:
if(
neighbour.x >= c_x - chunk_amount * 0.5
and neighbour.x <= c_x + chunk_amount * 0.53
and neighbour.y >= c_z - chunk_amount * 0.5
and neighbour.y <= c_z + chunk_amount * 0.53
and not neighbour in used
):
stack.append(neighbour)
func clean_up_chunks():
for key in chunks:
var chunk = chunks[key]
if chunk.should_remove:
chunk.queue_free()
chunks.erase(key)
func reset_chunks():
for key in chunks:
chunks[key].should_remove = true

File diff suppressed because one or more lines are too long

@ -0,0 +1,12 @@
# Blender MTL File: 'None'
# Material Count: 1
newmtl Default_OBJ
Ns 250.000000
Ka 1.000000 1.000000 1.000000
Kd 0.800000 0.800000 0.800000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.450000
d 1.000000
illum 2

@ -0,0 +1,32 @@
# Blender v3.2.2 OBJ File: ''
# www.blender.org
mtllib grass.mtl
o grass_triangle
v 0.012754 0.302056 0.000000
v 0.000000 0.402742 0.000000
v -0.012754 0.302056 0.000000
v 0.025508 0.201371 0.000000
v -0.038262 0.100685 0.000000
v 0.038262 0.100685 0.000000
v -0.051016 0.000000 0.000000
v 0.051016 0.000000 0.000000
v -0.025508 0.201371 0.000000
vt 0.621057 0.520384
vt 0.497980 0.979655
vt 0.376628 0.520384
vt 0.744133 0.238847
vt 0.133923 0.098911
vt 0.867209 0.098911
vt 0.012570 0.013663
vt 0.990286 0.013663
vt 0.255275 0.238847
vn 0.0000 -0.0000 1.0000
usemtl Default_OBJ
s 1
f 1/1/1 2/2/1 3/3/1
f 4/4/1 5/5/1 6/6/1
f 6/6/1 7/7/1 8/8/1
f 1/1/1 9/9/1 4/4/1
f 4/4/1 9/9/1 5/5/1
f 6/6/1 5/5/1 7/7/1
f 1/1/1 3/3/1 9/9/1

@ -0,0 +1,20 @@
[remap]
importer="wavefront_obj"
type="Mesh"
path="res://.import/grass.obj-b367f86df935fca9f4b72c7070d3608f.mesh"
[deps]
files=[ "res://.import/grass.obj-b367f86df935fca9f4b72c7070d3608f.mesh" ]
source_file="res://world/grass.obj"
dest_files=[ "res://.import/grass.obj-b367f86df935fca9f4b72c7070d3608f.mesh", "res://.import/grass.obj-b367f86df935fca9f4b72c7070d3608f.mesh" ]
[params]
generate_tangents=true
scale_mesh=Vector3( 1, 1, 1 )
offset_mesh=Vector3( 0, 0, 0 )
octahedral_compression=true
optimize_mesh_flags=4286

@ -0,0 +1,29 @@
# Blender v2.83.0 OBJ File: ''
# www.blender.org
v -0.255078 0.000000 0.000000
v 0.255078 0.000000 0.000000
v 0.000000 2.013708 0.000000
v -0.127539 1.006854 0.000000
v 0.127539 1.006854 0.000000
v -0.191308 0.503427 0.000000
v 0.063769 1.510281 0.000000
v -0.063769 1.510281 0.000000
v 0.191308 0.503427 0.000000
vt 0.621057 0.520384
vt 0.497980 0.979655
vt 0.376628 0.520384
vt 0.744133 0.238847
vt 0.133923 0.098911
vt 0.867209 0.098911
vt 0.012570 0.013663
vt 0.990286 0.013663
vt 0.255275 0.238847
vn 0.0000 0.0000 1.0000
s 1
f 7/1/1 3/2/1 8/3/1
f 5/4/1 6/5/1 9/6/1
f 9/6/1 1/7/1 2/8/1
f 7/1/1 4/9/1 5/4/1
f 5/4/1 4/9/1 6/5/1
f 9/6/1 6/5/1 1/7/1
f 7/1/1 8/3/1 4/9/1

@ -0,0 +1,20 @@
[remap]
importer="wavefront_obj"
type="Mesh"
path="res://.import/grass_triangle.obj-46b3ab3674e3d39ea8727b7c5a77c4a9.mesh"
[deps]
files=[ "res://.import/grass_triangle.obj-46b3ab3674e3d39ea8727b7c5a77c4a9.mesh" ]
source_file="res://world/grass_triangle.obj"
dest_files=[ "res://.import/grass_triangle.obj-46b3ab3674e3d39ea8727b7c5a77c4a9.mesh", "res://.import/grass_triangle.obj-46b3ab3674e3d39ea8727b7c5a77c4a9.mesh" ]
[params]
generate_tangents=true
scale_mesh=Vector3( 1, 1, 1 )
offset_mesh=Vector3( 0, 0, 0 )
octahedral_compression=true
optimize_mesh_flags=4286

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

@ -0,0 +1,77 @@
shader_type spatial;
render_mode cull_disabled, unshaded;
uniform float wind_speed = 0.2;
uniform float wind_strength = 2.0;
// How big, in world space, is the noise texture
// wind will tile every wind_texture_tile_size
uniform float wind_texture_tile_size = 20.0;
uniform float wind_vertical_strength = 0.3;
uniform vec2 wind_horizontal_direction = vec2(1.0, 0.5);
uniform sampler2D color_ramp : hint_black_albedo;
// we need a tiling noise here!
uniform sampler2D wind_noise : hint_black;
uniform vec3 character_position;
uniform float character_radius = 3.0;
uniform sampler2D character_distance_falloff_curve : hint_black_albedo;
uniform float character_push_strength = 1.0;
varying float debug_wind;
void vertex() {
vec3 world_vert = (WORLD_MATRIX * vec4(VERTEX, 1.0)).xyz;
vec2 normalized_wind_direction = normalize(wind_horizontal_direction);
vec2 world_uv = world_vert.xz / wind_texture_tile_size + normalized_wind_direction * TIME * wind_speed;
// we displace only the top part of the mesh
// note that this means that the mesh needs to have UV in a way that the bottom of UV space
// is at the top of the mesh
float displacement_affect = (1.0 - UV.y);
float wind_noise_intensity = (textureLod(wind_noise, world_uv, 0.0).r - 0.5);
// We convert the direction of the wind into vertex space from world space
// if we used it directly in vertex space, rotated blades of grass wouldn't behave properly
vec2 vert_space_horizontal_dir =
(inverse(WORLD_MATRIX) * vec4(wind_horizontal_direction, 0.0, 0.0)).xy;
vert_space_horizontal_dir = normalize(vert_space_horizontal_dir);
vec3 bump_wind = vec3(
wind_noise_intensity * vert_space_horizontal_dir.x,
1.0 - wind_noise_intensity,
wind_noise_intensity * vert_space_horizontal_dir.y);
normalize(bump_wind);
bump_wind *= vec3(wind_strength, wind_vertical_strength, wind_strength);
VERTEX += bump_wind * displacement_affect;
// At the moment the blades are pushed away in a perfectly circular manner.
// We could distort the distance to the character based on a noise, to break a bit the
// circular shape. We could distort the falloff by sampling in a noise based on the xz coordinates.
// The task is left to the reader
vec3 dir_to_character = character_position - WORLD_MATRIX[3].xyz;
// uncomment the following line to have a horizontal only character push
//dir_to_character.y = 0.0;
float distance_to_character = length(dir_to_character);
float falloff = 1.0 - smoothstep(0.0, 1.0, distance_to_character/character_radius);
// Because we operate in vertex space, we need to convert the direction to the character
// in vertex space. Otherwise, it wouldn't work for rotated blades of grass.
// comment the next line to observe how the blades are not all facing away from the character.
dir_to_character = (inverse(WORLD_MATRIX) * vec4(dir_to_character, 0.0)).xyz;
dir_to_character = normalize(dir_to_character);
// sample the curve based on how far we are from the character, in normalized coordinates
float falloff_curve = texture(character_distance_falloff_curve, vec2(falloff)).x;
// direction to character is inverted because we want to point away from it
VERTEX += normalize(-dir_to_character) * falloff_curve * character_push_strength * displacement_affect;
}
void fragment() {
ALBEDO = texture(color_ramp, vec2(1.0 - UV.y, 0)).rgb ;
}

@ -0,0 +1,40 @@
[gd_resource type="ShaderMaterial" load_steps=8 format=2]
[ext_resource path="res://world/materials/wind_grass.shader" type="Shader" id=1]
[sub_resource type="Curve" id=1]
_data = [ Vector2( 0, 0 ), 0.0, 2.71765, 0, 0, Vector2( 1, 1 ), -0.129412, 0.0, 0, 0 ]
[sub_resource type="CurveTexture" id=2]
width = 128
curve = SubResource( 1 )
[sub_resource type="Gradient" id=3]
offsets = PoolRealArray( 0, 0.486339, 0.966102 )
colors = PoolColorArray( 0.054902, 0.556863, 0.439216, 1, 0.321569, 0.886275, 0.341176, 1, 0.498039, 0.921569, 0.356863, 1 )
[sub_resource type="GradientTexture" id=4]
gradient = SubResource( 3 )
[sub_resource type="OpenSimplexNoise" id=5]
period = 109.1
persistence = 0.138
lacunarity = 0.44
[sub_resource type="NoiseTexture" id=6]
seamless = true
noise = SubResource( 5 )
[resource]
shader = ExtResource( 1 )
shader_param/wind_speed = 0.2
shader_param/wind_strength = 2.0
shader_param/wind_texture_tile_size = 20.0
shader_param/wind_vertical_strength = 0.3
shader_param/wind_horizontal_direction = Vector2( 1, 0.5 )
shader_param/character_position = Vector3( 0, 1.22062, 0 )
shader_param/character_radius = 3.0
shader_param/character_push_strength = 1.0
shader_param/color_ramp = SubResource( 4 )
shader_param/wind_noise = SubResource( 6 )
shader_param/character_distance_falloff_curve = SubResource( 2 )
Loading…
Cancel
Save