-
Notifications
You must be signed in to change notification settings - Fork 5
/
scml2moai.py
executable file
·199 lines (165 loc) · 7 KB
/
scml2moai.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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
#!/usr/bin/env python
#
# scml2moai.py
# sprinter-moai
#
# Created by Jon Baker on 2012-11-09.
# Distributed under MPL-2.0 licence (http://opensource.org/licenses/MPL-2.0)
#
# TODO: fix rotations use spin when collecting data
# TODO: Work out bone multipliers
import xml.sax
import sys
import re
from collections import OrderedDict
from optparse import OptionParser
class SaxHandler (xml.sax.handler.ContentHandler):
def __init__ (self):
self.tags = []
self.folders = {}
self.folder = 0
self.files = {}
self.animations = {}
self.animation_name = None
self.timelines = OrderedDict()
self.timeline = 0
self.key = 0
def startElement (self, name, attrs):
# Track current tag
self.tags.append(name)
# Parse SCML Information
if name == 'folder':
self.folder = attrs.getValue('id')
elif name == 'file':
self.files[attrs.getValue('id')] = { 'file': re.sub(r'.*/', '', attrs.getValue('name')), 'width': attrs.getValue('width'), 'height': attrs.getValue('height') }
elif name == 'animation':
# Store each animation
loop = False if attrs.has_key('looping') and attrs.getValue('looping') == 'false' else True
self.animation_name = attrs.getValue('name')
self.animations[self.animation_name] = { 'loop' : loop , 'length' : attrs.getValue('length') }
elif name == 'object_ref' and self.tags[-3] == "mainline":
# Get zindexes from mainline and store by [timesline][key]
timeline = attrs.getValue('timeline')
keys = self.timelines[timeline] if self.timelines.has_key(timeline) else OrderedDict()
keys[attrs.getValue('key')] = { 'zindex': attrs.getValue('z_index') }
self.timelines[timeline] = keys
elif name == "timeline":
self.timeline = attrs.getValue('id')
elif name == "key" and self.tags[-2] == "timeline":
# Collect useful information from key tag and stash keyframe
self.key = attrs.getValue('id')
if self.timelines.has_key(self.timeline) and self.timelines[self.timeline].has_key(self.key):
keyframe = self.timelines[self.timeline][self.key]
keyframe['spin'] = attrs.getValue('spin') if attrs.has_key('spin') else 0
keyframe['time'] = attrs.getValue('time') if attrs.has_key('time') else 0
self.timelines[self.timeline][self.key] = keyframe
elif name == "object" and self.tags[-3] == "timeline":
# retrieve keyframe and populate with data from object tag
if self.timelines.has_key(self.timeline) and self.timelines[self.timeline].has_key(self.key):
keyframe = self.timelines[self.timeline][self.key]
file_details = self.folders[attrs.getValue('folder')][attrs.getValue('file')]
keyframe['texture'] = file_details['file']
keyframe['x'] = attrs.getValue('x') if attrs.has_key('x') else 0
keyframe['y'] = attrs.getValue('y') if attrs.has_key('y') else 0
keyframe['angle'] = attrs.getValue('angle') if attrs.has_key('angle') else 0
keyframe['scale_x'] = attrs.getValue('scale_x') if attrs.has_key('scale_x') else 1
keyframe['scale_y'] = attrs.getValue('scale_y') if attrs.has_key('scale_y') else 1
keyframe['pivot_x'] = (float(file_details['width']) * float(attrs.getValue('pivot_x'))) if attrs.has_key('pivot_x') else 0
keyframe['pivot_y'] = (float(file_details['height']) * float(attrs.getValue('pivot_y'))) if attrs.has_key('pivot_y') else float(file_details['height'])
self.timelines[self.timeline][self.key] = keyframe
def endElement (self, name):
self.tags.pop()
if name == 'folder':
# store all files for ending folder
self.folders[self.folder] = self.files
self.files = {}
elif name == "animation":
# store mainline for ending animation
self.animations[self.animation_name] = self.timelines #OrderedDict(sorted(self.timelines.items(), key=lambda t: t[0]))
self.timelines = {}
#---
def output_lua(anim_data, outpath, scale):
# Open file for writing
f = open(outpath, 'w')
print "Writing animation to %s" % outpath
f.write('local anim = {\n')
for i, animation in enumerate(anim_data.keys()):
# Generate each animation
timelines = anim_data[animation]
f.write('\t[\'%s\'] = {\n' % animation )
for ii, timeline in enumerate(timelines.values()):
# Generate Each object layer
f.write('\t\t[%d] = {\n' % (ii + 1))
for iii, keyframe in enumerate(timeline.values()):
# Generate each keyframe
f.write('\t\t\t[%d] = {\n' % (iii + 1))
iv = 0
for anim_property, value in keyframe.items():
# Generate each property
iv += 1
if anim_property in ["x", "y", "pivot_x", "pivot_y"]:
# Adjust values for scaled textures
adjusted_val = int(round(float(value) * scale))
f.write('\t\t\t\t[\'%s\'] = %s' % (anim_property, adjusted_val))
elif anim_property == "texture":
f.write('\t\t\t\t[\'%s\'] = \'%s\'' % (anim_property, value))
else:
f.write('\t\t\t\t[\'%s\'] = %s' % (anim_property, value))
# write our closing brackets
f.write(',\n' if iv < len(keyframe.keys()) else '\n')
f.write('\t\t\t},\n' if iii+1 < len(timeline) else '\t\t\t}\n')
f.write('\t\t},\n' if ii+1 < len(timelines) else '\t\t}\n')
f.write('\t},\n' if i+1 < len(anim_data.keys()) else '\t}\n')
f.write('}\n\nreturn anim')
# Close file
f.close
def main ():
try:
# Parser setup for command line options
parser = OptionParser(usage = "usage: %prog file.scml [options]", version = "SCML2Moai v0.1")
parser.add_option("-f", "--file", dest="filename",
help="specify output FILE (default outputs to same name as scml file)", metavar="FILE")
parser.add_option("-4", "--x4",
action="store_true", dest="x4", default=False,
help="Dimensions are based on @4x size also output @2x.lua and standard .lua files")
parser.add_option("-2", "--x2",
action="store_true", dest="x2", default=False,
help="Dimensions are based on @2x size also output standard .lua files")
(options, args) = parser.parse_args()
# Check an input file has been provided
if len(args) >= 1:
inpath= args[0]
else:
parser.error("You must specify an SCML file to convert")
# Identify filenames for lua files
outpath = ""
outpathx2 = None
outpathx4 = None
if options.filename:
outpath = options.filename
elif re.search(r"\.scml\Z", inpath.lower()):
outpath = re.sub(r"\.scml\Z", ".lua", inpath.lower())
else:
outpath = inpath + ".lua"
if options.x2 or options.x4:
outpathx2 = re.sub(r"\.lua\Z", "@2x.lua", outpath)
if options.x4:
outpathx4 = re.sub(r"\.lua\Z", "@4x.lua", outpath)
# Parse input scml file
handler= SaxHandler()
xml.sax.parse (inpath, handler)
anim_data = handler.animations
# Write data to lua files
if options.x4:
output_lua(anim_data, outpathx4, 1)
output_lua(anim_data, outpathx2, 0.5)
output_lua(anim_data, outpath, 0.25)
elif options.x2:
output_lua(anim_data, outpathx2, 1)
output_lua(anim_data, outpath, 0.5)
else:
output_lua(anim_data, outpath, 1)
except IOError as e:
print "Error: {0}".format(e.strerror)
if __name__ == '__main__':
main()