Generate 3D Terrain in python using random noise and marching cube

Generate 3D Terrain in python using random noise and marching cube


import numpy as np
from skimage import measure
from scipy.ndimage import interpolation
import os
import bpy

# noise = PerlinNoise(octaves=4, seed=3)


# Step 1
def generate_random_3d_array() -> np.ndarray:
    """generate 3d numpy array

    Returns:
        np.ndarray: [description]
    """
    # for every x and y there should be certain z value
    width = 10
    length = 10
    height = 6
    # all ones 3d array
    chunk = np.ones((width, height, length))
    for x in range(width):
        for z in range(length):
            # random height for our 3D terrain
            y = np.random.randint(1, 4)
            chunk[x, :y, z] = 0
            # all values below our height will be Zeros

    chunk = chunk.astype("uint8")

    # use interpolation to change scaling of 3D object
    # increasing scale factor will smooth the 3D terrain
    scale = (10, 2, 10)
    chunk = interpolation.zoom(chunk, scale)
    # print(chunk)
    return chunk


# TODO Create Cave in 3D Terrain
def create_cave(chunk: np.ndarray) -> np.ndarray:
    """[summary]

    Args:
        chunk (np.ndarray): [description]

    Returns:
        np.ndarray: [description]
    """
    chunk[20:60, 1: 5, 11: 35] = 1
    chunk[45:55, 5: 10, 25: 30] = 1  # CAVE Entrance
    return chunk


def save_obj(vertices, faces, normals, path: str, name: str):
    """Save 3D Terrain as obj file

    Args:
        vertices ([type]): [description]
        faces ([type]): [description]
        normals ([type]): [description]
        path (str): path of the new file
        name (str): name of the new file
    """
    faces = faces + 1  # Make Indexes of vertices start from 1

    with open(os.path.join(path, name)+".obj", 'w') as newObj:
        for v in vertices:
            newObj.write("v {0} {1} {2}\n".format(v[0], v[1], v[2]))

        for n in normals:
            newObj.write("vn {0} {1} {2}\n".format(n[0], n[1], n[2]))

        for f in faces:
            newObj.write("f {0}//{0} {1}//{1} {2}//{2}\n".format(f[0],
                                                                 f[1],
                                                                 f[2]))


def smart_uv_unwrp(name: str) -> None:
    """UV Unwrap for texture using blender

    Args:
        name (str): name of saved obj file
    """
    file_loc = os.path.join("obj", name)+".obj"

    obj_object = bpy.ops.import_scene.obj(filepath=file_loc)
    obj_object = bpy.context.selected_objects[0]

    if obj_object.type == 'MESH':
        # bpy.context.scene.objects.active = obj_object
        bpy.ops.object.editmode_toggle()  # entering edit mode
        bpy.ops.mesh.select_all(action='SELECT')  # select all objects elements
        bpy.ops.uv.smart_project()  # the actual unwrapping operation
        bpy.ops.object.editmode_toggle()  # exiting edit mode
        bpy.ops.export_scene.obj(filepath=file_loc)


def generate_random_chunk(name: str) -> None:
    random_3d = generate_random_3d_array()
    # random_3d = create_cave(random_3d)

    # Use marching cubes to obtain the surface mesh of these ellipsoids
    # Marching cube will convert numpy array to 3D Terrain
    verts, faces, normals, values = measure.marching_cubes(random_3d, 0)

    # save obj
    save_obj(verts, faces, normals, "obj", name)

    # load obj in blender and convert obj uV unwrap using blender
    smart_uv_unwrp(name)


name = "land2"
print("Creating Random 3D terrain")
generate_random_chunk(name)
print("Done")

No comments:

Post a Comment

Building a CLI-Based People Tracking and Dwell Time Analytics System Using YOLOv8 and DeepSORT

  Introduction Tracking people across video frames and analyzing their behavior (like  dwell time ) is a crucial task for many real-world ap...