Partially moved CPU-intensive code to C++ modules OpenSimplex and Voxel

master
Marc Gilleron 2016-05-05 02:20:01 +02:00
parent 30305d5384
commit f17f202609
3 changed files with 127 additions and 175 deletions

View File

@ -2,14 +2,13 @@
[ext_resource path="res://block_node.gd" type="Script" id=1] [ext_resource path="res://block_node.gd" type="Script" id=1]
[node name="Block" type="MeshInstance"] [node name="Block" type="MeshInstance"]
_import_transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ) _import_transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 )
layers = 1 layers = 1
geometry/visible = true geometry/visible = true
geometry/material_override = null geometry/material_override = null
geometry/cast_shadow = true geometry/cast_shadow = 1
geometry/receive_shadows = true geometry/receive_shadows = true
geometry/range_begin = 0.0 geometry/range_begin = 0.0
geometry/range_end = 0.0 geometry/range_end = 0.0

View File

@ -17,7 +17,7 @@ params/blend_mode = 0
params/depth_draw = 1 params/depth_draw = 1
params/line_width = 0.0 params/line_width = 0.0
fixed_flags/use_alpha = false fixed_flags/use_alpha = false
fixed_flags/use_color_array = false fixed_flags/use_color_array = true
fixed_flags/use_point_size = false fixed_flags/use_point_size = false
fixed_flags/discard_alpha = false fixed_flags/discard_alpha = false
fixed_flags/use_xy_normalmap = false fixed_flags/use_xy_normalmap = false
@ -87,7 +87,7 @@ ambient_light/color = Color( 0.378906, 0.378906, 0.378906, 1 )
ambient_light/energy = 1.0 ambient_light/energy = 1.0
fxaa/enabled = false fxaa/enabled = false
background/mode = 2 background/mode = 2
background/color = Color( 0.539063, 0.525474, 0.414825, 1 ) background/color = Color( 0.446167, 0.645425, 0.671875, 1 )
background/energy = 1.0 background/energy = 1.0
background/scale = 1.0 background/scale = 1.0
background/glow = 0.0 background/glow = 0.0
@ -115,7 +115,7 @@ hdr/exposure_adj_speed = 0.5
fog/enabled = true fog/enabled = true
fog/begin = 0.01 fog/begin = 0.01
fog/begin_color = Color( 0, 0, 0, 1 ) fog/begin_color = Color( 0, 0, 0, 1 )
fog/end_color = Color( 0.537255, 0.521569, 0.411765, 1 ) fog/end_color = Color( 0.443137, 0.643137, 0.670588, 1 )
fog/attenuation = 0.707107 fog/attenuation = 0.707107
fog/bg = true fog/bg = true
bcs/enabled = false bcs/enabled = false
@ -129,7 +129,7 @@ srgb/enabled = false
[node name="Camera" type="Camera" parent="."] [node name="Camera" type="Camera" parent="."]
_import_transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ) _import_transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 )
transform/local = Transform( 0.815509, -0.351713, 0.459612, 0, 0.794154, 0.607717, -0.578744, -0.495599, 0.64764, 18.1809, 4.88061, 25.3826 ) transform/local = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 4, 0 )
projection = 0 projection = 0
fov = 80.0 fov = 80.0
near = 0.1 near = 0.1
@ -154,7 +154,7 @@ _import_transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 )
layers = 1 layers = 1
geometry/visible = true geometry/visible = true
geometry/material_override = null geometry/material_override = null
geometry/cast_shadow = false geometry/cast_shadow = 0
geometry/receive_shadows = true geometry/receive_shadows = true
geometry/range_begin = 0.0 geometry/range_begin = 0.0
geometry/range_end = 0.0 geometry/range_end = 0.0
@ -228,7 +228,7 @@ environment = SubResource( 3 )
[node name="DirectionalLight" type="DirectionalLight" parent="."] [node name="DirectionalLight" type="DirectionalLight" parent="."]
_import_transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ) _import_transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 )
transform/local = Transform( 0.817159, -0.383981, 0.429895, 4.66628e-006, 0.745816, 0.666152, -0.576412, -0.54435, 0.609452, 1.36405, 1.98931, 7.37862 ) transform/local = Transform( -0.761692, 0.482016, -0.432995, -0.609208, -0.305189, 0.731932, 0.220658, 0.821291, 0.526108, 1.11878, 2.33452, 7.6244 )
layers = 1 layers = 1
params/enabled = true params/enabled = true
params/editor_only = false params/editor_only = false
@ -236,7 +236,7 @@ params/bake_mode = 0
params/energy = 1.0 params/energy = 1.0
colors/diffuse = Color( 1, 1, 1, 1 ) colors/diffuse = Color( 1, 1, 1, 1 )
colors/specular = Color( 1, 1, 1, 1 ) colors/specular = Color( 1, 1, 1, 1 )
shadow/shadow = true shadow/shadow = false
shadow/darkening = 0.0 shadow/darkening = 0.0
shadow/z_offset = 0.05 shadow/z_offset = 0.05
shadow/z_slope_scale = 0.0 shadow/z_slope_scale = 0.0
@ -245,7 +245,7 @@ shadow/blur_passes = 1.0
projector = null projector = null
operator = 0 operator = 0
shadow/mode = 3 shadow/mode = 3
shadow/max_distance = 100.0 shadow/max_distance = 32.0
shadow/split_weight = 0.5 shadow/split_weight = 0.5
shadow/zoffset_scale = 2.0 shadow/zoffset_scale = 2.0

View File

@ -22,20 +22,9 @@ const SORT_TIME = 1
export(Material) var solid_material = null export(Material) var solid_material = null
export(Material) var transparent_material = null export(Material) var transparent_material = null
var view_radius = 8 var view_radius = 4
var min_y = -2 var min_y = -4
var max_y = 2 var max_y = 4
var VoxelType = preload("voxel_type.gd")
var _side_normals = [
Vector3(-1,0,0),
Vector3(1,0,0),
Vector3(0,-1,0),
Vector3(0,1,0),
Vector3(0,0,-1),
Vector3(0,0,1)
]
var _blocks = {} var _blocks = {}
var _generating_blocks = {} var _generating_blocks = {}
@ -50,11 +39,14 @@ var _priority_positions = []
var _outer_positions = [] var _outer_positions = []
var _precalc_neighboring = [] var _precalc_neighboring = []
var _noise = OsnNoise.new()
var _mesh_builder = VoxelMeshBuilder.new()
var _library = VoxelLibrary.new()
# BLOCK_SIZE * BLOCK_SIZE * BLOCK_SIZE voxel buffer used in VoxelMaps
class Block: class Block:
var voxel_map = null var voxel_map = null
var voxels = [] var voxels = VoxelBuffer.new()
var pos = Vector3(0,0,0) var pos = Vector3(0,0,0)
var mesh = null var mesh = null
var node = null var node = null
@ -64,24 +56,7 @@ class Block:
var need_update = false var need_update = false
func _init(): func _init():
pass voxels.create(BLOCK_SIZE+2,BLOCK_SIZE+2,BLOCK_SIZE+2)
#voxels = create_voxel_grid(BLOCK_SIZE+2,BLOCK_SIZE+2,BLOCK_SIZE+2)
static func create_voxel_grid(sx,sy,sz):
var grid = []
grid.resize(sz)
for z in range(0, sz):
var plane = []
plane.resize(sy)
grid[z] = plane
for y in range(0, sy):
var line = []
#var line = IntArray()
line.resize(sx)
plane[y] = line
for x in range(0, sx):
line[x] = 0
return grid
func is_generated(): func is_generated():
return has_generated and has_structures return has_generated and has_structures
@ -131,6 +106,10 @@ class BlockRequest:
func _ready(): func _ready():
_noise.set_seed(131183)
_library.set_atlas_size(4)
_camera = get_parent().get_node("Camera") _camera = get_parent().get_node("Camera")
_load_voxel_types() _load_voxel_types()
@ -149,40 +128,16 @@ func _precalculate_neighboring():
_precalc_neighboring.append(Vector3(x,y,z)) _precalc_neighboring.append(Vector3(x,y,z))
func add_voxel_type(id, name):
var vt = VoxelType.new(id, name)
if id >= _voxel_types.size():
_voxel_types.resize(id+1)
_voxel_types[id] = vt
return vt
func _load_voxel_types(): func _load_voxel_types():
add_voxel_type(0, "air") \ _library.create_voxel(0, "air").set_transparent()
.set_geom(VoxelType.GEOM_EMPTY) \ _library.create_voxel(1, "grass_dirt").set_cube_geometry().set_cube_uv_tbs_sides(Vector2(0,0), Vector2(0,1), Vector2(1,0))
.set_transparent(true) _library.create_voxel(2, "dirt").set_cube_geometry().set_cube_uv_all_sides(Vector2(1,0))
_library.create_voxel(3, "log").set_cube_geometry().set_cube_uv_tbs_sides(Vector2(3,0), Vector2(3,0), Vector2(2,0))
_library.create_voxel(4, "water").set_transparent().set_cube_geometry(15.0/16.0).set_cube_uv_all_sides(Vector2(2,1)).set_material_id(1)
add_voxel_type(1, "grassy_dirt") \ _mesh_builder.set_library(_library)
.set_tbs_atlas_pos(Vector2(0,0), Vector2(1,0), Vector2(0,1)) _mesh_builder.set_material(solid_material, 0)
_mesh_builder.set_material(transparent_material, 1)
add_voxel_type(2, "bush") \
.set_all_atlas_pos(Vector2(1,1)) \
.set_geom(VoxelType.GEOM_XQUAD) \
.set_transparent(true)
add_voxel_type(3, "log") \
.set_tbs_atlas_pos(Vector2(3,0), Vector2(3,0), Vector2(2,0))
add_voxel_type(4, "dirt") \
.set_all_atlas_pos(Vector2(1,0))
add_voxel_type(5, "water") \
.set_all_atlas_pos(Vector2(2,1)) \
.set_transparent(true) \
.set_geom(VoxelType.GEOM_LIQUID)
for vt in _voxel_types:
vt.compile()
func _precalculate_priority_positions(): func _precalculate_priority_positions():
@ -276,8 +231,11 @@ func _process(delta):
# Closer blocks are loaded first # Closer blocks are loaded first
var pos = _pending_blocks[_pending_blocks.size()-1] var pos = _pending_blocks[_pending_blocks.size()-1]
_pending_blocks.pop_back() _pending_blocks.pop_back()
_thread.start(self, "generate_block_thread", BlockRequest.new(pos, BlockRequest.TYPE_GENERATE))
_generating_blocks[pos] = true _generating_blocks[pos] = true
var arg = BlockRequest.new(pos, BlockRequest.TYPE_GENERATE)
#_thread.start(self, "generate_block_thread", arg)
#print("generate " + str(pos))
spawn_block(generate_block(arg.block_pos))
# Visible blocks are loaded first # Visible blocks are loaded first
# var hbs = Vector3(0.5, 0.5, 0.5) * BLOCK_SIZE # var hbs = Vector3(0.5, 0.5, 0.5) * BLOCK_SIZE
@ -296,6 +254,7 @@ func generate_block_thread(request):
var block = generate_block(request.block_pos) var block = generate_block(request.block_pos)
# Call the main thread to wait # Call the main thread to wait
call_deferred("thread_finished") call_deferred("thread_finished")
#_generating_blocks.erase(block.pos) # Enable only without thread!
return block return block
else: else:
print("Unknown request type " + str(request.type)) print("Unknown request type " + str(request.type))
@ -310,40 +269,22 @@ func thread_finished():
func generate_block(pos): func generate_block(pos):
var time_before = OS.get_ticks_msec() var time_before = OS.get_ticks_msec()
#var time_before = OS.get_ticks_msec() var block = Block.new()
var voxels = Block.create_voxel_grid(BLOCK_SIZE+2, BLOCK_SIZE+2, BLOCK_SIZE+2) block.pos = pos
#print("Create: " + str(OS.get_ticks_msec() - time_before) + "ms")
#time_before = OS.get_ticks_msec() #time_before = OS.get_ticks_msec()
var empty = generate_random(voxels, pos * BLOCK_SIZE) var empty = generate_3d(block.voxels, pos * BLOCK_SIZE)
#print("Generate: " + str(OS.get_ticks_msec() - time_before) + "ms") #print("Generate: " + str(OS.get_ticks_msec() - time_before) + "ms")
var mesh = null var mesh = null
if empty: if empty:
voxels = null block.voxels = null
else: else:
#time_before = OS.get_ticks_msec() #time_before = OS.get_ticks_msec()
var st_solid = SurfaceTool.new() mesh = _mesh_builder.build(block.voxels)
var st_transparent = SurfaceTool.new()
st_solid.begin(Mesh.PRIMITIVE_TRIANGLES)
st_transparent.begin(Mesh.PRIMITIVE_TRIANGLES)
st_solid.set_material(solid_material)
st_transparent.set_material(transparent_material)
make_mesh(voxels, st_solid, st_transparent)
#st.index()
mesh = st_solid.commit()
st_transparent.commit(mesh)
#print("Bake: " + str(OS.get_ticks_msec() - time_before) + "ms") #print("Bake: " + str(OS.get_ticks_msec() - time_before) + "ms")
var block = Block.new()
block.voxel_map = self block.voxel_map = self
block.voxels = voxels
block.pos = pos
block.mesh = mesh block.mesh = mesh
block.gen_time = OS.get_ticks_msec() - time_before block.gen_time = OS.get_ticks_msec() - time_before
@ -353,17 +294,80 @@ func generate_block(pos):
func spawn_block(block): func spawn_block(block):
if block.mesh != null: if block.mesh != null:
var mesh_instance = preload("res://block.tscn").instance() var mesh_instance = preload("res://block.tscn").instance()
mesh_instance.set_mesh(block.mesh)
mesh_instance.set_translation(block.pos * BLOCK_SIZE) mesh_instance.set_translation(block.pos * BLOCK_SIZE)
mesh_instance.spawn()
mesh_instance.set_mesh(block.mesh)
mesh_instance.voxel_map = self mesh_instance.voxel_map = self
add_child(mesh_instance) add_child(mesh_instance)
block.node = mesh_instance block.node = mesh_instance
mesh_instance.spawn()
_blocks[block.pos] = block _blocks[block.pos] = block
print("Gen time: " + str(block.gen_time) + " (empty=" + str(block.mesh == null) + ")") #print("Gen time: " + str(block.gen_time) + " (empty=" + str(block.mesh == null) + ")")
func generate_random(cubes, offset): func generate_test(cubes, offset):
cubes.set_voxel(1, 1,1,1)
cubes.set_voxel(1, 3,1,1)
cubes.set_voxel(1, 3,1,2)
cubes.set_voxel(1, 5,1,1)
cubes.set_voxel(1, 5,1,2)
cubes.set_voxel(1, 5,2,1)
cubes.set_voxel(1, 8,1,1)
cubes.set_voxel(1, 8,2,1)
cubes.set_voxel(1, 7,1,1)
cubes.set_voxel(1, 11,1,1)
cubes.set_voxel(1, 11,2,1)
cubes.set_voxel(1, 10,1,1)
cubes.set_voxel(1, 10,1,2)
for x in range(4,7):
for z in range(4,7):
cubes.set_voxel(1, x, 2, z)
cubes.set_voxel(1, x+5, 2, z)
cubes.set_voxel(1, 5,3,5)
cubes.set_voxel(1, 5,1,5)
return false
func generate_3d(cubes, offset):
var ox = offset.x
var oy = offset.y
var oz = offset.z
var empty = true
var bs = cubes.get_size_x()
var noise1 = OsnFractalNoise.new()
noise1.set_source_noise(_noise)
noise1.set_period(100)
noise1.set_octaves(4)
var dirt = 1
if oy < 0:
dirt = 2
for z in range(0, bs):
for x in range(0, bs):
for y in range(0, bs):
var gy = y+oy
var h = noise1.get_noise_3d(x+ox+2, gy, z+oz)
if h < 1-gy*0.01 - 1:
cubes.set_voxel(dirt, x, y, z)
empty = false
else:
if gy < 0:
cubes.set_voxel(4, x, y, z)
else:
cubes.set_voxel(0, x, y, z)
empty = false
return empty
func generate_heightmap(cubes, offset):
var ox = offset.x var ox = offset.x
var oy = offset.y var oy = offset.y
var oz = offset.z var oz = offset.z
@ -373,31 +377,33 @@ func generate_random(cubes, offset):
var dirt = 1 var dirt = 1
if oy < 0: if oy < 0:
dirt = 4 dirt = 2
var air = 0
if oy < 0:
air = 5
var bs = cubes.size() var bs = cubes.get_size_x()
var noise1 = OsnFractalNoise.new()
noise1.set_source_noise(_noise)
noise1.set_period(128)
noise1.set_octaves(4)
for z in range(0, bs): for z in range(0, bs):
for x in range(0, bs): for x in range(0, bs):
#var h = 8.0*(cos((ox+x)/8.0) + sin((oz+z)/8.0)) + 8 - oy
var n1 = preload("Simplex.gd").simplex2(ns1*(ox+x), ns1*(oz+z)) var h = 16.0 * noise1.get_noise_2d(ox+x, oz+z) - oy
var n2 = preload("Simplex.gd").simplex2(ns2*(ox+x+100.0), ns2*(oz+z))
var h = 16.0*n1 + 4.0*n2 + 8 - oy
if h >= 0: if h >= 0:
if h < bs: if h < bs:
empty = false empty = false
for y in range(0, h): for y in range(0, h):
cubes[z][y][x] = dirt cubes.set_voxel(dirt, x,y,z)
#cubes[z][y][x] = dirt
for y in range(h, bs): for y in range(h, bs):
cubes[z][y][x] = air cubes.set_voxel(0, x,y,z)
if oy == -BLOCK_SIZE: #cubes[z][y][x] = air
cubes[z][bs-1][x] = 0 # if oy == -BLOCK_SIZE:
if oy >= 0 and randf() < 0.2: # cubes[z][bs-1][x] = 0
cubes[z][h][x] = 2 # if oy >= 0 and randf() < 0.2:
# cubes[z][h][x] = 2
# if randf() < 0.01: # if randf() < 0.01:
# var th = h+1+randi()%8 # var th = h+1+randi()%8
# if th > bs: # if th > bs:
@ -407,67 +413,14 @@ func generate_random(cubes, offset):
else: else:
empty = false empty = false
for y in range(0, bs): for y in range(0, bs):
cubes[z][y][x] = 1 cubes.set_voxel(dirt, x,y,z)
else: else:
for y in range(0, bs): for y in range(0, bs):
cubes[z][y][x] = air cubes.set_voxel(0, x,y,z)
return empty return empty
func _is_face_visible(vt, other_vt):
return other_vt.id == 0 or (other_vt.is_transparent and other_vt != vt)
func make_mesh(cubes, st_solid, st_transparent):
# Note: the data must be padded with border voxels,
# so iteration starts at 1 and there is no need to check boundaries.
# This trades performance over a bit of memory.
for z in range(1, cubes.size()-1):
var plane = cubes[z]
for y in range(1, plane.size()-1):
var line = plane[y]
for x in range(1, line.size()-1):
var voxel_id = line[x]
if voxel_id != 0:
var voxel_type = _voxel_types[voxel_id]
var st = st_solid
if voxel_type.is_transparent:
st = st_transparent
var ppos = Vector3(x,y,z)
var pos = ppos-Vector3(1,1,1)
# Side faces (full cubes only have side faces)
if voxel_type.model_side_vertices.size() != 0:
for side in range(0,6):
var npos = ppos + _side_normals[side]
if _is_face_visible(voxel_type, _voxel_types[cubes[npos.z][npos.y][npos.x]]):
st.add_normal(_side_normals[side])
var uvs = voxel_type.model_side_uv[side]
var vertices = voxel_type.model_side_vertices[side]
for vi in range(0,vertices.size()):
st.add_uv(uvs[vi])
st.add_vertex(pos + vertices[vi])
if voxel_type.geom_type == VoxelType.GEOM_XQUAD:
pos.x += rand_range(-0.15, 0.15)
pos.z += rand_range(-0.15, 0.15)
# Model faces
if voxel_type.model_vertices.size() != 0:
var vertices = voxel_type.model_vertices
var uvs = voxel_type.model_uv
var normals = voxel_type.model_normals
for vi in range(0, vertices.size()):
st.add_uv(uvs[vi])
st.add_normal(normals[vi])
st.add_vertex(pos + vertices[vi])