-
Notifications
You must be signed in to change notification settings - Fork 0
/
generate_stones.py
98 lines (81 loc) · 3.51 KB
/
generate_stones.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
import random
import numpy as np
from scipy.spatial import ConvexHull
import argparse
parser=argparse.ArgumentParser(
prog="Generate Stones",
description="A script to procedurally generate rock meshes and collisionboxes for use in Minetest"
)
#parser.add_argument("scale_min",type=float, help="Lower random limit for the scale of the boulders.",default=0.75)
#parser.add_argument("scale_max",type=float, help="Upper random limit for the scale of the boulders.",default=1.25)
#parser.add_argument("scale_max",type=float, help="Upper random limit for the scale of the boulders.",default=1.25)
#
# Algorithm Explanation:
# Step 1: Given randomly spaced 3d points, generate a convex hull too be used as the rock model
# Step 2: Save the ConvexHull returned by scipy into a .obj file.
# Step 3: Generate collision boxes for each vertex with an Axis Aligned Bounding Box starting from the origin (0,0,0) and ending at each point.
# Step 4: Save the collision boxes.
#
# Parameters:
# 1. Filename: String (not including .obj at the end)
# 2. Points: a numpy array with a size of (n, 3), n being the number of points
def save_stone_obj(filename,points):
mean_of_points=points.mean(axis=0)
points-=mean_of_points
hull=ConvexHull(points)
verts=hull.vertices
faces=hull.simplices
new_faces=[]
normals=[]
for idx,i in enumerate(faces):
normal=np.cross(points[i[0]]-points[i[1]],points[i[1]]-points[i[2]])
mean_of_face=points[i].mean(axis=0)
dotproduct=np.sign(normal.dot(mean_of_face))
if dotproduct<0:
new_faces.append(i[[1,0,2]])
else:
new_faces.append(i)
#normal=-torch.randn(3,)
normals.append(normal)
with open("models/"+filename+".obj","w") as file:
for i in points:
file.write(f"v {i[0]} {i[1]} {i[2]}\n")
for i in normals:
file.write(f"vn {i[0]} {i[1]} {i[2]}\n")
for i in faces:
file.write(f"vt {1} {0}\n")
file.write(f"vt {0} {1}\n")
file.write(f"vt {0} {0}\n")
for i,face in enumerate(faces):
file.write(f"f {new_faces[i][0]+1}/{i*3+1}/{i+1} {new_faces[i][1]+1}/{i*3+2}/{i+1} {new_faces[i][2]+1}/{i*3+3}/{i+1}\n")
num_boxes=0
with open("nodeboxes/"+filename+".lua","w") as file:
file.write("return {")
for i in verts:
min_pos=np.concatenate((np.array([[0,0,0]]),np.expand_dims(points[i],axis=0))).min(axis=0)
max_pos=np.concatenate((np.array([[0,0,0]]),np.expand_dims(points[i],axis=0))).max(axis=0)
min_pos[0]*=-1
max_pos[0]*=-1
file.write("{"+f"{min_pos[0]}, {min_pos[1]}, {min_pos[2]}, {max_pos[0]}, {max_pos[1]}, {max_pos[2]}"+"},\n")
file.write("}")
print("Faces:",len(faces))
print("Verts:",len(verts))
if __name__=="__main__":
#Wall Stones
for i in range(10):
name="stonevertical"+str(i)
scale=np.array([0.7,1.4,0.7])
num_points=random.randrange(20,70)
points=np.random.randn(num_points,3)
points=points/(np.expand_dims(np.linalg.norm(points,axis=1),axis=1))*scale
save_stone_obj(name,points)
print(name, scale, num_points)
#Boulders
for i in range(10):
name="stone"+str(i)
scale=random.uniform(0.75,1.25)
num_points=int(scale*50)
points=np.random.randn(num_points,3)
points=points/(np.expand_dims(np.linalg.norm(points,axis=1),axis=1))*scale
save_stone_obj(name,points)
print(name, scale, num_points)