Skip to content
This repository was archived by the owner on Oct 28, 2022. It is now read-only.

Commit a5dbeb8

Browse files
committed
wip validity checks placement
1 parent 23151ac commit a5dbeb8

File tree

7 files changed

+302
-377
lines changed

7 files changed

+302
-377
lines changed

.env

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
PYTHONPATH=lib/python3.7/site-packages

Entities/LevelGeometry.py

+56-150
Original file line numberDiff line numberDiff line change
@@ -1,162 +1,68 @@
1+
import trimesh
12
import numpy as np
2-
import os
3-
4-
if "DEBUG" in os.environ and os.environ["DEBUG"] == 'PLOT':
5-
import plotly.offline as py
6-
import plotly.graph_objs as go
7-
8-
import math
9-
10-
class Face:
11-
def __init__(self, vertices, triangle_index, collision_type):
12-
self.vertices = np.array(vertices)
13-
self.index = triangle_index
14-
self.vertices_transposed = np.transpose(self.vertices)
15-
16-
self.normal = np.array([0.0, 0.0, 0.0])
17-
self.center = np.array([0.0, 0.0, 0.0])
18-
self.type = None
19-
self.collision_type = collision_type
20-
self.bounding_box = None
21-
22-
self.calc_props()
23-
24-
def calc_props(self):
25-
[p1, p2, p3] = self.vertices
26-
u = p2 - p1
27-
v = p3 - p1
28-
29-
# calc normal
30-
cross = np.cross(u, v)
31-
#print(cross)
32-
biggest_var = np.max(np.abs(cross))
33-
34-
if biggest_var != 0:
35-
self.normal = cross / np.max(np.abs(cross))
36-
37-
is_floor = self.normal[1] > 0.01
38-
is_ceiling = self.normal[1] < -0.01
39-
40-
if is_floor:
41-
self.type = 'FLOOR'
42-
elif is_ceiling:
43-
self.type = 'CEILING'
44-
else:
45-
self.type = 'WALL'
46-
47-
self.center = np.mean(self.vertices, axis=0, dtype=float)
48-
self.bounding_box = (
49-
np.min(self.vertices_transposed[0]), # -X
50-
np.max(self.vertices_transposed[0]), # +X
51-
np.min(self.vertices_transposed[1]), # -Y
52-
np.max(self.vertices_transposed[1]), # +Y
53-
np.min(self.vertices_transposed[2]), # -Z
54-
np.max(self.vertices_transposed[2]), # +Z
55-
)
56-
57-
58-
class Geometry:
59-
def __init__(self, vertices, triangles, collision_type, index):
60-
self.vertices = np.array(vertices)
61-
self.triangles = np.array(triangles)
62-
self.faces = []
63-
self.faces_by_type = {'FLOOR': [], 'CEILING': [], 'WALL': []}
64-
self.collision_type = collision_type
65-
self.index = index
66-
67-
self.convert_to_faces()
68-
69-
def convert_to_faces(self):
70-
for face_index, vertices in enumerate(self.vertices[self.triangles]):
71-
face = Face(vertices, face_index, self.collision_type)
72-
self.faces.append(face)
73-
self.faces_by_type[face.type].append(face)
74-
75-
def plot_get_color(self):
76-
colors = []
77-
78-
colors = []
79-
type_colors = {
80-
'FLOOR': (0, 0, 1),
81-
'WALL': (0, 1, 0),
82-
'CEILING': (1, 0, 0),
83-
}
84-
for face in self.faces:
85-
colors.append(type_colors[face.type])
86-
return colors
87-
88-
def plot(self):
89-
mesh_components = np.transpose(self.vertices)
90-
triangle_indices = np.transpose(self.triangles)
91-
92-
if len(mesh_components) > 0:
93-
return go.Mesh3d(
94-
x=np.negative(mesh_components[0]), # x neg
95-
y=mesh_components[2], # y and z swapped
96-
z=mesh_components[1],
97-
i=triangle_indices[0],
98-
j=triangle_indices[1],
99-
k=triangle_indices[2],
100-
text=f'Collision Type: {hex(self.collision_type)}',
101-
facecolor=self.plot_get_color(),
102-
#flatshading=True,
103-
color='#FFB6C1',
104-
#hoverinfo="skip"
105-
)
106-
107-
3+
from random import choice
4+
#trimesh.util.attach_to_log()
1085

1096
class LevelGeometry:
1107
def __init__(self, level):
1118
self.level = level
1129
self.area_geometries = {}
113-
self.debug_points = []
10+
self.area_geometry_triangle_collision_types = {}
11+
self.area_face_types = {}
11412

115-
def add_area(self, area_id, vertices, triangles, collision_type):
116-
if area_id not in self.area_geometries:
117-
self.area_geometries[area_id] = []
13+
def get_collision_type_for_triangle(self, area_id, triangle_index):
14+
if area_id not in self.area_geometry_triangle_collision_types:
15+
raise Exception("Area-ID not in collision types dict")
11816

119-
if area_id == 0x2: return
120-
121-
geometry = Geometry(vertices, triangles, collision_type, len(self.area_geometries[area_id]))
122-
self.area_geometries[area_id].append(geometry)
17+
for ((start, end), collision_type) in self.area_geometry_triangle_collision_types[area_id].items():
18+
if triangle_index >= start and triangle_index <= end:
19+
return collision_type
12320

124-
def add_debug_marker(self, vec, obj, color=(0, 0, 0)):
125-
self.debug_points.append(np.array([*vec, obj.behaviour_name, color]))
21+
def get_random_point_in_area(self, area_id):
22+
if area_id not in self.area_face_types:
23+
raise Exception("Area-ID list geometry")
24+
25+
random_floor_triangle_index = choice(self.area_face_types[area_id]['FLOOR'])
26+
triangle_center = self.area_geometries[area_id].triangles_center[random_floor_triangle_index]
27+
28+
return list(triangle_center)
29+
12630

127-
def get_triangles(self, floor_type = None):
128-
faces = []
129-
for (area_id, areas) in self.area_geometries.items():
130-
for area in areas:
131-
if not floor_type:
132-
faces.extend(area.faces)
133-
else:
134-
faces.extend(area.faces_by_type[floor_type])
135-
return faces
31+
def add_area(self, area_id, vertices, triangles, collision_type):
32+
geometry = trimesh.Trimesh(vertices=vertices, faces=triangles, metadata=dict(collision=collision_type))
33+
#for facet in geometry.facets:
34+
# geometry.visual.face_colors[facet] = trimesh.visual.random_color()
13635

137-
def plot(self):
138-
traces = []
139-
for (area_id, areas) in self.area_geometries.items():
140-
for area in areas:
141-
traces.append(area.plot())
36+
geometry_triangle_count = self.area_geometries[area_id].triangles.shape[0] if area_id in self.area_geometries else 0
14237

143-
if len(self.debug_points):
144-
debug_transposed = np.transpose(self.debug_points)
145-
traces.append(
146-
go.Scatter3d(
147-
x=np.negative(debug_transposed[0].astype(float), dtype=float), # x neg
148-
y=debug_transposed[2], # y and z swapped
149-
z=debug_transposed[1],
150-
text=debug_transposed[3],
151-
mode="markers",
152-
marker=dict(
153-
sizemode='diameter',
154-
size=7,
155-
color=debug_transposed[4],
156-
#line=dict(color='rgb(140, 140, 170)')
157-
)
158-
)
159-
)
160-
161-
if len(traces) > 0:
162-
py.plot(traces, filename=f'dumps/level_plots/{self.level.name}.html', auto_open=False)
38+
if area_id not in self.area_geometries:
39+
self.area_geometries[area_id] = geometry
40+
else:
41+
self.area_geometries[area_id] += geometry
42+
43+
if area_id not in self.area_geometry_triangle_collision_types:
44+
self.area_geometry_triangle_collision_types[area_id] = {}
45+
46+
if area_id not in self.area_face_types:
47+
self.area_face_types[area_id] = {
48+
'FLOOR': [],
49+
'WALL': [],
50+
'CEILING': []
51+
}
52+
53+
for triangle_index, normal in enumerate(geometry.face_normals):
54+
tri_type = 'WALL'
55+
56+
if normal[1] > 0.01:
57+
tri_type = 'FLOOR'
58+
elif normal[1] < -0.01:
59+
tri_type = 'CEILING'
60+
61+
self.area_face_types[area_id][tri_type].append(triangle_index)
62+
63+
self.area_geometry_triangle_collision_types[area_id][(
64+
geometry_triangle_count, # start
65+
geometry_triangle_count + len(triangles) # end
66+
)] = collision_type
67+
68+
#geometry.show()

Entities/Object3D.py

+5-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
class Object3D(BaseMemoryRecord):
55
source: str # SPECIAL_MACRO_OBJ, PLACE_OBJ, MACRO_OBJ, MARIO_SPAWN
66
model_id: str
7+
current_area: int
78
level: "Level" = None
89
position: tuple = (0, 0, 0) # (X, Y, Z)
910
rotation: tuple = (0, 0, 0) # (X, Y, Z)
@@ -35,10 +36,11 @@ def remove(self, rom):
3536
rom.levelscripts[self.level].remove_special_macro_object(self)
3637

3738

38-
def __init__(self, source, model_id, position, level, rotation = None, behaviour = None, bparams = [], mem_address = None):
39+
def __init__(self, source, area_id, model_id, position, level, rotation = None, behaviour = None, bparams = [], mem_address = None):
3940
super().__init__()
4041

4142
self.source = source
43+
self.area_id = area_id
4244
self.model_id = model_id
4345
self.position = position
4446
self.level = level
@@ -62,4 +64,5 @@ def __init__(self, source, model_id, position, level, rotation = None, behaviour
6264
#print(self)
6365

6466
def __str__(self):
65-
return f'Object3D: Source: {self.source}, Model-ID: {self.model_id}, position: {repr(self.position)}, rotation: {repr(self.rotation)}, bparams: {repr(self.bparams)}, bscript: {hex(self.behaviour or 0)}, mem_pos: {hex(self.mem_address)}'
67+
return self.generate_name() + '\n' + f'Source: {self.source}, In-Area: {self.area_id}, Model-ID: {self.model_id}, position: {repr(self.position)}, rotation: {repr(self.rotation)}, bparams: {repr(self.bparams)}, bscript: {hex(self.behaviour or 0)}, mem_pos: {hex(self.mem_address)}'
68+
#return f'Object3D: Source: {self.source}, Model-ID: {self.model_id}, position: {repr(self.position)}, rotation: {repr(self.rotation)}, bparams: {repr(self.bparams)}, bscript: {hex(self.behaviour or 0)}, mem_pos: {hex(self.mem_address)}'

Parsers/LevelScript.py

+14-13
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ def __init__(self, rom : 'ROM', start : int, end : int = None, layer = 0, level
3737
self.mario_spawn = None
3838
self.layer = layer
3939
self.level = level
40-
self.current_area = None
40+
self.current_area = 0x0
4141
self.water_boxes = []
4242

4343
self.macro_tables = {}
@@ -205,7 +205,7 @@ def process(self, start, end):
205205
(b1, b2, b3, b4) = tuple([self.rom.read_integer(cursor + 16 + n) for n in range(4)])
206206
b_script = self.rom.read_integer(cursor + 20, 4)
207207

208-
self.objects.append(Object3D("PLACE_OBJ", model_id, position, self.level, rotation, b_script, [b1, b2, b3, b4], cursor + 2))
208+
self.objects.append(Object3D("PLACE_OBJ", self.current_area, model_id, position, self.level, rotation, b_script, [b1, b2, b3, b4], cursor + 2))
209209
elif command.identifier == 0x39:
210210
if self.rom.rom_type == 'EXTENDED':
211211
""" 0x39 PLACE_MACRO_OBJECTS """
@@ -253,7 +253,7 @@ def process(self, start, end):
253253
spawn_area_id = self.rom.read_integer(cursor + 2)
254254
rotation_y = self.rom.read_integer(cursor + 4, 2, True)
255255
position = (self.rom.read_integer(cursor + 6, 2, True), self.rom.read_integer(cursor + 8, 2, True), self.rom.read_integer(cursor + 10, 2, True))
256-
self.objects.append(Object3D("MARIO_SPAWN", None, position, self.level, (None, rotation_y, None), mem_address = cursor + 2))
256+
self.objects.append(Object3D("MARIO_SPAWN", spawn_area_id, None, position, self.level, (None, rotation_y, None), mem_address = cursor + 2))
257257
#print(self.level.name, spawn_area_id, rotation_y, position)
258258
elif command.identifier == 0x20:
259259
""" 0x20 END_AREA """
@@ -375,7 +375,7 @@ def process_macro_objects(self, start, end):
375375
preset = preset_table[preset_id]
376376
position = (self.rom.read_integer(None, 2, True), self.rom.read_integer(None, 2, True), self.rom.read_integer(None, 2, True))
377377
(bparam1, bparam2) = (self.rom.read_integer(None, 1), self.rom.read_integer(None, 1))
378-
object3d = Object3D("MACRO_OBJ", preset.model_id, position, self.level, (None, rot_y, None), preset.behaviour_addr, mem_address = cursor, bparams=[bparam1, bparam2])
378+
object3d = Object3D("MACRO_OBJ", self.current_area, preset.model_id, position, self.level, (None, rot_y, None), preset.behaviour_addr, mem_address = cursor, bparams=[bparam1, bparam2])
379379
objects_found.append(object3d)
380380
macro_table["entries"].append(dict(
381381
object3d=object3d,
@@ -582,6 +582,7 @@ def process_special_objects_level(self, start, end):
582582
cursor += entry_length
583583

584584
triangles.append(indices)
585+
#print(self.level.name)
585586
#print(f'{len(triangles)} {hex(cd_type_int)} ({format_binary(cd_type)})')
586587
self.level_geometry.add_area(self.current_area, vertices, triangles, cd_type_int)
587588
collision_entry["triangle_bytes"] = triangle_bytes
@@ -620,18 +621,18 @@ def process_special_objects_level(self, start, end):
620621
if length == 8:
621622
model_id = preset.model_id
622623
position = (self.rom.read_integer(None, 2, True), self.rom.read_integer(None, 2, True), self.rom.read_integer(None, 2, True))
623-
entry = Object3D("SPECIAL_MACRO_OBJ", model_id, position, self.level, None, preset.behaviour_addr, mem_address = cursor)
624+
entry = Object3D("SPECIAL_MACRO_OBJ", self.current_area, model_id, position, self.level, None, preset.behaviour_addr, mem_address = cursor)
624625
elif length == 10:
625626
model_id = preset.model_id
626627
position = (self.rom.read_integer(None, 2, True), self.rom.read_integer(None, 2, True), self.rom.read_integer(None, 2, True))
627628
rotation = (None, self.rom.read_integer(None, 2, True), None)
628-
entry = Object3D("SPECIAL_MACRO_OBJ", model_id, position, self.level, rotation, preset.behaviour_addr, mem_address = cursor)
629+
entry = Object3D("SPECIAL_MACRO_OBJ", self.current_area, model_id, position, self.level, rotation, preset.behaviour_addr, mem_address = cursor)
629630
elif length == 12:
630631
model_id = preset.model_id
631632
position = (self.rom.read_integer(None, 2, True), self.rom.read_integer(None, 2, True), self.rom.read_integer(None, 2, True))
632633
rotation = (None, self.rom.read_integer(None, 2, True), None)
633634
(b1, b2) = (self.rom.read_integer(), self.rom.read_integer())
634-
entry = Object3D("SPECIAL_MACRO_OBJ", model_id, position, self.level, rotation, preset.behaviour_addr, [b1, b2], mem_address = cursor)
635+
entry = Object3D("SPECIAL_MACRO_OBJ", self.current_area, model_id, position, self.level, rotation, preset.behaviour_addr, [b1, b2], mem_address = cursor)
635636
else:
636637
raise Exception("Invalid Preset Length")
637638

@@ -672,12 +673,12 @@ def process_special_objects_level(self, start, end):
672673
elif water_box_id == 0x32 or water_box_id == 0xF0:
673674
water_box_type = "TOXIC"
674675

675-
water_box = (
676-
water_box_id,
677-
water_box_start_x, water_box_start_z,
678-
water_box_end_x, water_box_end_z,
679-
water_box_y,
680-
water_box_type
676+
water_box = dict(
677+
box_id=water_box_id,
678+
start=(water_box_start_x, water_box_y, water_box_start_z),
679+
end=(water_box_end_x, -8192.0, water_box_end_z),
680+
type=water_box_type,
681+
area_id=self.current_area
681682
)
682683
self.water_boxes.append(water_box)
683684
special_macro_table["waterboxes"].append(dict(

0 commit comments

Comments
 (0)