Skip to content

Commit f332c17

Browse files
committed
Started work on PC11 support and added various features
1 parent b4337a3 commit f332c17

15 files changed

+1883
-222
lines changed

Diff for: CMakeLists.txt

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
set(CMAKE_C_STANDARD 11)
22

3-
add_executable(parseRez parseRez.c)
3+
add_executable(pba-rez rez.c)
4+
add_executable(pba-csv csv.c json.h)
45
add_executable(format0001 format0001.c)
56
add_executable(format0022 format0022.c)
67
target_link_libraries(format0022 txc_dxtn)
78
add_executable(format0024 format0024.c)
8-
add_executable(format002A format002A.c)
9+
add_executable(format002A format002A.c json.h)
910
add_executable(format002C format002C.c)
11+
target_link_libraries(format002C m)
12+
add_executable(format008D format008D.c)

Diff for: README.md

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
This is a set of tools to operate on The Pinball Arcade (PBA) files.
2+
PBA is a video-game developed by [FarSight Studios](http://www.farsightstudios.com/).
3+
4+
# PBA Coordinate System
5+
6+
Their coordinate system seems to be (from a players perspective):
7+
8+
+X = Right
9+
+Y = Forward
10+
+Z = Up
11+
12+
The playfield center (including vertical elements) is usually located at 0.0, 0.0, 0.0.
13+
14+
Each unit appears to be roughly 400/3 inch (guessed).
15+
So a 20.5" x 42.0" playfield would be 2733 x 5600 in game units.
16+
17+
Human-readable angles are usually in degree, machine readable are usually in radians.
18+
19+
# PBA Bugs
20+
21+
2704/29: Missing alpha

Diff for: blender.py

+321
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,321 @@
1+
import bpy
2+
import os
3+
import json
4+
5+
context = bpy.context
6+
7+
models_path = "./converted"
8+
files_path = "."
9+
10+
11+
#context.space_data.show_backface_culling = True
12+
13+
14+
#create a scene
15+
scene = bpy.data.scenes.new("Import")
16+
camera_data = bpy.data.cameras.new("Camera")
17+
18+
camera = bpy.data.objects.new("Camera", camera_data)
19+
camera.location = (-2.0, 3.0, 3.0)
20+
camera.rotation_euler = (422.0, 0.0, 149)
21+
scene.objects.link(camera)
22+
23+
# do the same for lights etc
24+
25+
scene.update()
26+
27+
# make a new scene with cam and lights linked
28+
scene.camera = camera
29+
context.screen.scene = scene
30+
context.scene.render.engine = 'BLENDER_GAME'
31+
32+
def fileContents(filename):
33+
with open(filename) as f:
34+
return f.read()
35+
36+
37+
enums = json.loads(fileContents("./enums.json")) # Load _Enums file [as json]
38+
objs = json.loads(fileContents("./table.json")) # Load table file [as json]
39+
dil = json.loads(fileContents("./dil.json")) # Load Placement file [as json]
40+
41+
def resourceIndexFromName(name):
42+
return enums[name]['index']
43+
44+
def placementFromName(name):
45+
for placement in dil['objects']:
46+
a = placement['name']
47+
b = name
48+
if a == b:
49+
return placement
50+
if a.lower() == b.lower():
51+
print("Case does not match!")
52+
return placement
53+
print("Could not find '" + name + "'")
54+
return None
55+
56+
def layer(index):
57+
lm = [False] * 20
58+
lm[index] = True
59+
return lm
60+
61+
f = 0.001
62+
def importMesh(resourceName, fileIndex):
63+
resourceIndex = resourceIndexFromName(resourceName)
64+
filename = str(resourceIndex) + "-" + str(fileIndex) + ".obj"
65+
66+
path = os.path.join(models_path, filename)
67+
print("Loading " + filename)
68+
69+
# for collisions [floating vertices are kept!]:
70+
#bpy.ops.import_scene.obj(filepath=path, split_mode='OFF', use_split_objects=True, axis_forward='Y', axis_up='Z', use_image_search=False, filter_glob="*.obj;*.mtl")
71+
# for visuals:
72+
bpy.ops.import_scene.obj(filepath=path, split_mode='ON', use_split_groups=True, axis_forward='Y', axis_up='Z', use_image_search=False, filter_glob="*.obj;*.mtl")
73+
74+
imported = bpy.context.selected_objects
75+
76+
77+
78+
print("imported:" + str(imported))
79+
#bpy.data.objects[]
80+
for i in imported:
81+
i.scale *= f
82+
83+
return imported
84+
85+
def dilPlaceInstance(imported, placement):
86+
for i in imported:
87+
p = placement['position']
88+
xyz = (f * p[0], f * p[1], f * p[2])
89+
print(str(xyz))
90+
i.location = xyz
91+
r = placement['angle']
92+
i.rotation_mode = 'YXZ';
93+
i.rotation_euler = (r[0], r[2], r[1])
94+
s = placement['scale']
95+
#i.scale = (f * s[0], f * s[1], f * s[2]) # Doesn't seem to be used?!
96+
97+
def dilPlace(imported, obj):
98+
99+
# Check if this is placed using DIL or directly
100+
try:
101+
dilPos = obj['DilPos']
102+
except:
103+
dilPos = None
104+
105+
if dilPos == None:
106+
#FIXME: TODO
107+
print("Want this at standard loc")
108+
placement = obj['Pos']
109+
dilPlaceInstance(imported, placement)
110+
#"Pos": {
111+
# "position": [ -620.434143, 1590.591309, -200.085876 ],
112+
# "scale": [ 1.000000, 1.000000, 1.000000 ],
113+
# "angle": [ 0.000000, 0.000000, 0.000000 ]
114+
#},
115+
116+
else:
117+
#FIXME: TODO
118+
print("pos: " + str(dilPos))
119+
var = placementFromName(dilPos['variable'])
120+
if var == None:
121+
#FIXME: Delete object now?!
122+
return
123+
placements = var['placements']
124+
for placement in placements:
125+
print("Want this at " + str(placement))
126+
# FIXME: Place this object at all locations?!
127+
placement = placements[0]
128+
dilPlaceInstance(imported, placement)
129+
130+
#print(objs)
131+
132+
# Iterate over objects and find PBObject entries
133+
for key in objs:
134+
obj = objs[key]
135+
if obj['type'] != "PBObject":
136+
continue
137+
138+
obj_object = obj['Object']
139+
140+
print("Parsing " + key)
141+
142+
# Mark bad objects
143+
144+
bad = True
145+
t = obj_object['type']
146+
l = 9
147+
148+
# Visual only
149+
if t == "PB_OBJECTTYPE_VISUAL": l = 0
150+
151+
# Collision and colliding trigger
152+
if t == "PB_OBJECTTYPE_COLLISION": l = 0
153+
if t == "PB_OBJECTTYPE_TRAP": l = 0
154+
155+
# Playfield
156+
if t == "PB_OBJECTTYPE_FLOOR": l = 1
157+
158+
# Lamps
159+
if t == "PB_OBJECTTYPE_LAMPSET": l = 2
160+
161+
# Unknown
162+
if t == "PB_OBJECTTYPE_UNIQUE": l = 3
163+
164+
# Game elements
165+
if t == "PB_OBJECTTYPE_PLUNGER": l = 4
166+
if t == "PB_OBJECTTYPE_FLIPPER": l = 4
167+
if t == "PB_OBJECTTYPE_SLINGSHOT": l = 4
168+
if t == "PB_OBJECTTYPE_POPBUMPER": l = 4
169+
if t == "PB_OBJECTTYPE_SPINNER": l = 4
170+
if t == "PB_OBJECTTYPE_TARGET": l = 4
171+
if t == "PB_OBJECTTYPE_KICKER": l = 4
172+
if t == "PB_OBJECTTYPE_STOPPER": l = 4
173+
174+
if t == "PB_OBJECTTYPE_DIVERTER_ONOFF": l = 4 # FIXME: Does this also have visuals?!
175+
176+
# Any object on layer >= 5 doesn't have a collision!
177+
178+
# Game elements (which should be without collision)
179+
if t == "PB_OBJECTTYPE_GATE": l = 5
180+
if t == "PB_OBJECTTYPE_GATE_ONEWAY": l = 5
181+
if t == "PB_OBJECTTYPE_WIRE": l = 5
182+
183+
# Triggers
184+
if t == "PB_OBJECTTYPE_MAGNET": l = 6
185+
if t == "PB_OBJECTTYPE_OPTO": l = 7
186+
if t == "PB_OBJECTTYPE_BALLDRAIN": l = 8
187+
188+
189+
190+
191+
192+
#Not rendering: PB_OBJECTTYPE_BALLDRAIN
193+
#Not rendering: PB_OBJECTTYPE_COLLISION
194+
#Not rendering: PB_OBJECTTYPE_DIVERTER_ONOFF
195+
#Not rendering: PB_OBJECTTYPE_FLIPPER
196+
#Not rendering: PB_OBJECTTYPE_FLOOR
197+
#Not rendering: PB_OBJECTTYPE_GATE
198+
#Not rendering: PB_OBJECTTYPE_GATE_ONEWAY
199+
#Not rendering: PB_OBJECTTYPE_KICKER
200+
#Not rendering: PB_OBJECTTYPE_LAMPSET
201+
#Not rendering: PB_OBJECTTYPE_MAGNET
202+
#Not rendering: PB_OBJECTTYPE_OPTO
203+
#Not rendering: PB_OBJECTTYPE_PLUNGER
204+
#Not rendering: PB_OBJECTTYPE_PLUNGER_EXIT
205+
#Not rendering: PB_OBJECTTYPE_POPBUMPER
206+
#Not rendering: PB_OBJECTTYPE_SLINGSHOT
207+
#Not rendering: PB_OBJECTTYPE_SPINNER
208+
#Not rendering: PB_OBJECTTYPE_STOPPER
209+
#Not rendering: PB_OBJECTTYPE_TARGET
210+
#Not rendering: PB_OBJECTTYPE_TRAP
211+
#Not rendering: PB_OBJECTTYPE_UNIQUE
212+
#Not rendering: PB_OBJECTTYPE_VISUAL
213+
#Not rendering: PB_OBJECTTYPE_WIRE
214+
215+
216+
#if t == "PB_OBJECTTYPE_WIRE": l = 3
217+
#if t == "PB_OBJECTTYPE_WIRE": l = 3
218+
#if t == "PB_OBJECTTYPE_WIRE": l = 3
219+
#if t == "PB_OBJECTTYPE_WIRE": l = 3
220+
if bad:
221+
print("Not rendering: " + t)
222+
#for i in imported:
223+
# i.layers = [False] * 20
224+
# i.layers[19] = True
225+
#continue
226+
227+
228+
# Load collisions
229+
try:
230+
collision = obj['Collision']
231+
except:
232+
collision = None
233+
234+
#collision = None
235+
236+
if collision:
237+
ct = collision['type']
238+
if ct == 'Sphere':
239+
#FIXME: collision['mode'] should be "Manual"
240+
x,y,z = collision['position']
241+
radius = collision['radius']
242+
#FIXME: Create a new sphere object
243+
#for i in imported:
244+
# i.layers[0] = False
245+
# i.layers[1] = True
246+
#continue
247+
#imported =
248+
249+
bpy.ops.mesh.primitive_ico_sphere_add(subdivisions=2, size=f * radius, view_align=False, enter_editmode=False, location=(f * x, f * y, f * z))
250+
bpy.ops.object.shade_smooth()
251+
imported = bpy.context.selected_objects
252+
253+
# FIXME: Set physics type to sphere
254+
255+
elif ct == 'Mesh':
256+
imported = []
257+
for mesh in collision['data']:
258+
imported.extend(importMesh(mesh['resource'], mesh['index']))
259+
print("Imported append: " + str(imported))
260+
else:
261+
print("Damnit! Unsupported collision type: '" + ct + "'")
262+
imported = None
263+
264+
if imported:
265+
print(key + " => " + str(imported))
266+
dilPlace(imported, obj)
267+
for i in imported:
268+
i.layers = layer(l + 10)
269+
i.hide_render = True
270+
i.draw_type = 'SOLID'
271+
if l >= 5:
272+
i.game.physics_type = 'NO_COLLISION'
273+
274+
275+
# Iterate over each sub-model
276+
try:
277+
models = obj['Models']
278+
except:
279+
models = None
280+
281+
if models:
282+
for model in models:
283+
284+
imported = importMesh(model['resource'], model['index'])
285+
for i in imported:
286+
i.layers = layer(l)
287+
i.game.physics_type = 'NO_COLLISION'
288+
289+
for i in imported:
290+
for m in i.material_slots:
291+
print("Slot: " + str(m))
292+
m.material.game_settings.alpha_blend = 'ALPHA'
293+
#FIXME: Only do this if the linked texture has alpha.. otherwise this breaks >.<
294+
295+
#meshes = [c for c in imported if c.type == 'MESH']
296+
#for m in meshes:
297+
# m.scale *= 0.001
298+
299+
dilPlace(imported, obj)
300+
301+
302+
303+
# Resize the entire scene
304+
# FIXME: Should be done while creating the scene or by setting a different scale of the scene
305+
306+
# FIXME: Set material to alpha
307+
308+
# Only show visuals
309+
scene.layers = [True] * 10 + [False] * 10
310+
311+
print("dir: " + str(scene.game_settings.gravity))
312+
print("used: " + str(scene.use_gravity))
313+
scene.game_settings.physics_gravity = (0.0, -0.5, -10.0) # Bugs out!
314+
315+
if False:
316+
cams = [c for c in context.scene.objects if c.type == 'CAMERA']
317+
for c in cams:
318+
context.scene.camera = c
319+
print("Render ", model_path, context.scene.name, c.name)
320+
context.scene.render.filepath = "somepathmadeupfrommodelname"
321+
bpy.ops.render.render(write_still=True)

0 commit comments

Comments
 (0)