from ursina import *
from ursina.shaders import lit_with_shadows_shader
random.seed(0)

app = Ursina()

Entity(model='cube', y=150, color=color.red)

# Normally I'd load a heightmap texture, but for this example I'll include a heightmap texture in the script itself, as base64.
data = '''data:image/png;base64, '''
heightmap = Texture(data)

terrain_model = Terrain(heightmap)
Entity.default_shader = lit_with_shadows_shader
# print(terrain_model.height_values.tolist())
terrain = Entity(model=terrain_model, texture='grass', texture_scale=(3,3), scale=128)
# Instead of using a texture on the terrain, map them to vertex colors.
# Later, we'll use these colors to tint the grass we place, so the grass match the ground.
for uv in terrain.model.uvs:
    x = uv[0] * terrain.texture_scale[0]
    y = uv[1] * terrain.texture_scale[1]
    x -= int(x)
    y -= int(y)
    x = int(x * terrain.texture.width)
    y = int(y * terrain.texture.height)
    terrain.model.colors.append(terrain.texture.get_pixel(x, y).tint(-.25))

terrain.model.generate()
terrain.texture = None

scene.fog_density = .003


# Use two quads crossing each other to create a grass model
quad_model = load_model('quad', use_deepcopy=True)
grass_models = [deepcopy(quad_model) for i in range(4)]
from ursina.ursinamath import rotate_around_point_2d
for i, mesh in enumerate(grass_models):
    p = Entity()
    Entity(parent=p, model='quad', rotation_y=i*(360/5), origin_y=-.5, scale=1.5)
    Entity(parent=p, model='quad', rotation_y=(i*(360/5))+90, origin_y=-.5, scale=1.5)
    p.combine()
    grass_models[i] = p.model

# Instead of generating blue noise from scratch, which could be slow, use a pre-generated looping pattern to get the same effect
blue_noise = Empty(
    width = 64,
    height = 64,
    pattern = [(40, 1), (47, 1), (22, 2), (8, 3), (16, 3), (58, 4), (29, 6), (51, 6), (38, 7), (45, 8), (15, 9), (4, 10), (23, 10), (34, 12), (9, 13), (53, 13), (60, 13), (42, 14), (19, 16), (3, 17), (31, 17), (51, 18), (12, 19), (24, 19), (38, 19), (45, 21), (61, 21), (6, 23), (16, 24), (29, 24), (55, 25), (36, 26), (21, 27), (49, 27), (42, 28), (61, 28), (4, 29), (12, 30), (33, 31), (25, 32), (52, 33), (2, 34), (45, 34), (59, 34), (38, 35), (11, 37), (28, 37), (19, 38), (4, 39), (34, 39), (48, 40), (54, 41), (61, 41), (22, 44), (29, 44), (8, 45), (37, 45), (42, 45), (14, 46), (1, 47), (51, 47), (58, 50), (25, 51), (32, 51), (46, 51), (5, 52), (19, 52), (12, 54), (38, 54), (54, 55), (28, 57), (62, 57), (6, 58), (41, 58), (48, 58), (20, 59), (13, 61), (56, 62)]
)

perlin_noise_pixels = load_texture('perlin_noise').pixels


density = .8
grass_parent = Entity(texture='grass_lump', double_sided=True, model='grass_combined')
grass_colors = list(set([colr.tint(.2) for colr in terrain.model.colors]))

def place_grass():
    for __X in range(-8,8,1):
        for __Z in range(-8,8,1):

            for pos in blue_noise.pattern:
                if random.random() < 1-density:
                    continue

                x = ((__X * blue_noise.width)  + pos[0]) * .125
                z = ((__Z * blue_noise.height) + pos[1]) * .125
                y, n = terraincast(Vec3(x,300,z), terrain, return_normals=True)
                if not y:
                    continue

                # if sample_bilinear(perlin_noise_pixels, x, y, clamp_if_outside=False) < .5:
                #     continue

                grass_parent.model.vertices.extend([(v*1)+Vec3(x,y,z) for v in random.choice(grass_models).vertices])

                random_color = random.choice(grass_colors)
                # random_color = color.white

                grass_parent.model.colors.extend([random_color, ] * len(grass_models[0].vertices))
                grass_parent.model.uvs.extend(grass_models[0].uvs)

    grass_parent.model.generate()
    # Since placing the grass can be slow, save the result to disk and load it instead of generating it again next time.
    grass_parent.model.save('grass_combined.bam')

if not grass_parent.model:
    grass_parent.model = Mesh()
    place_grass()


player = EditorCamera(model=Capsule(height=1.5), origin_y=-.75, color=color.orange)
camera.fov = 100
player_speed = 8
def update():
    player.position += player.forward * (held_keys['w'] - held_keys['s']) * time.dt * player_speed
    player.position += player.right * (held_keys['d'] - held_keys['a']) * time.dt * player_speed
    player.y = terraincast(player.world_position+Vec3(0,100,0), terrain)



def input(key):
    if key == 'space':
        grass_parent.model = Mesh()
        place_grass()

Sky()
DirectionalLight().look_at(Vec3(1,-1,1))
# window.size = Vec2(1920,1080)
# window.borderless = True
# window.editor_ui.enabled = False

app.run()
