1 |
using System; |
2 |
using System.Collections.Generic; |
3 |
using System.IO; |
4 |
|
5 |
namespace Oni |
6 |
{ |
7 |
internal class DaeExporter : Exporter |
8 |
{ |
9 |
private readonly bool noAnimation; |
10 |
private readonly List<string> animationNames = new List<string>(); |
11 |
private readonly string geometryName; |
12 |
private readonly string fileType; |
13 |
|
14 |
public DaeExporter(string[] args, InstanceFileManager fileManager, string outputDirPath, string fileType) |
15 |
: base(fileManager, outputDirPath) |
16 |
{ |
17 |
foreach (string arg in args) |
18 |
{ |
19 |
if (arg == "-noanim") |
20 |
noAnimation = true; |
21 |
else if (arg.StartsWith("-anim:", StringComparison.Ordinal)) |
22 |
animationNames.Add(arg.Substring(6)); |
23 |
else if (arg.StartsWith("-geom:", StringComparison.Ordinal)) |
24 |
geometryName = arg.Substring(6); |
25 |
} |
26 |
|
27 |
this.fileType = fileType; |
28 |
} |
29 |
|
30 |
protected override void ExportFile(string sourceFilePath) |
31 |
{ |
32 |
string extension = Path.GetExtension(sourceFilePath); |
33 |
|
34 |
if (string.Equals(extension, ".xml", StringComparison.OrdinalIgnoreCase)) |
35 |
{ |
36 |
var sceneExporter = new SceneExporter(InstanceFileManager, OutputDirPath); |
37 |
sceneExporter.ExportScene(sourceFilePath); |
38 |
return; |
39 |
} |
40 |
|
41 |
base.ExportFile(sourceFilePath); |
42 |
} |
43 |
|
44 |
protected override List<InstanceDescriptor> GetSupportedDescriptors(InstanceFile file) |
45 |
{ |
46 |
var descriptors = new List<InstanceDescriptor>(); |
47 |
descriptors.AddRange(file.GetNamedDescriptors(TemplateTag.ONCC)); |
48 |
descriptors.AddRange(file.GetNamedDescriptors(TemplateTag.TRBS)); |
49 |
descriptors.AddRange(file.GetNamedDescriptors(TemplateTag.M3GM)); |
50 |
descriptors.AddRange(file.GetNamedDescriptors(TemplateTag.AKEV)); |
51 |
descriptors.AddRange(file.GetNamedDescriptors(TemplateTag.OBAN)); |
52 |
descriptors.AddRange(file.GetNamedDescriptors(TemplateTag.OFGA)); |
53 |
descriptors.AddRange(file.GetNamedDescriptors(TemplateTag.ONWC)); |
54 |
return descriptors; |
55 |
} |
56 |
|
57 |
protected override void ExportInstance(InstanceDescriptor descriptor) |
58 |
{ |
59 |
var tag = descriptor.Template.Tag; |
60 |
|
61 |
if (tag == TemplateTag.AKEV) |
62 |
{ |
63 |
var mesh = Akira.AkiraDatReader.Read(descriptor); |
64 |
Akira.AkiraDaeWriter.Write(mesh, descriptor.Name, OutputDirPath, fileType); |
65 |
return; |
66 |
} |
67 |
|
68 |
var scene = new Dae.Scene(); |
69 |
scene.Name = descriptor.Name; |
70 |
|
71 |
var textureWriter = new Motoko.TextureDaeWriter(OutputDirPath); |
72 |
var geometryWriter = new Motoko.GeometryDaeWriter(textureWriter); |
73 |
var bodyWriter = new Totoro.BodyDaeWriter(geometryWriter); |
74 |
|
75 |
if (tag == TemplateTag.OFGA) |
76 |
ExportObjectGeometry(descriptor, scene, geometryWriter); |
77 |
else if (tag == TemplateTag.OBAN) |
78 |
ExportObjectAnimation(descriptor, scene, geometryWriter); |
79 |
else if (tag == TemplateTag.ONCC) |
80 |
ExportCharacterBody(descriptor, scene, bodyWriter); |
81 |
else if (tag == TemplateTag.TRBS) |
82 |
ExportCharacterBodySet(descriptor, scene, bodyWriter); |
83 |
else if (tag == TemplateTag.M3GM) |
84 |
ExportGeometry(descriptor, scene, geometryWriter); |
85 |
else if (tag == TemplateTag.ONWC) |
86 |
ExportWeaponGeometry(descriptor, scene, geometryWriter); |
87 |
|
88 |
if (scene.Nodes.Count > 0) |
89 |
{ |
90 |
string filePath = Path.Combine(OutputDirPath, descriptor.Name + "." + fileType); |
91 |
|
92 |
Dae.Writer.WriteFile(filePath, scene); |
93 |
} |
94 |
} |
95 |
|
96 |
private void ExportObjectGeometry(InstanceDescriptor descriptor, Dae.Scene scene, Motoko.GeometryDaeWriter geometryWriter) |
97 |
{ |
98 |
var geometry = Physics.ObjectDatReader.ReadObjectGeometry(descriptor); |
99 |
var root = new Dae.Node(); |
100 |
|
101 |
foreach (var objNode in geometry.Geometries) |
102 |
root.Nodes.Add(geometryWriter.WriteNode(objNode.Geometry, objNode.Geometry.Name)); |
103 |
|
104 |
scene.Nodes.Add(root); |
105 |
} |
106 |
|
107 |
private void ExportObjectAnimation(InstanceDescriptor descriptor, Dae.Scene scene, Motoko.GeometryDaeWriter geometryWriter) |
108 |
{ |
109 |
var animation = Physics.ObjectDatReader.ReadAnimation(descriptor); |
110 |
Dae.Node node; |
111 |
|
112 |
if (geometryName == "camera") |
113 |
{ |
114 |
node = new Dae.Node |
115 |
{ |
116 |
Name = descriptor.Name + "_camera", |
117 |
Instances = { |
118 |
new Dae.CameraInstance { |
119 |
Target = new Dae.Camera { |
120 |
XFov = 45.0f, |
121 |
AspectRatio = 4.0f / 3.0f, |
122 |
ZNear = 1.0f, |
123 |
ZFar = 10000.0f |
124 |
} |
125 |
}} |
126 |
}; |
127 |
} |
128 |
else if (geometryName != null) |
129 |
{ |
130 |
var file = InstanceFileManager.OpenFile(geometryName); |
131 |
|
132 |
if (file == null) |
133 |
{ |
134 |
Console.Error.WriteLine("Cannot fine file {0}", geometryName); |
135 |
node = new Dae.Node(); |
136 |
} |
137 |
else |
138 |
{ |
139 |
var geom = Motoko.GeometryDatReader.Read(file.Descriptors[0]); |
140 |
node = geometryWriter.WriteNode(geom, geom.Name); |
141 |
} |
142 |
} |
143 |
else |
144 |
{ |
145 |
node = new Dae.Node(); |
146 |
} |
147 |
|
148 |
scene.Nodes.Add(node); |
149 |
|
150 |
ExportAnimation(node, new List<Physics.ObjectAnimationKey>(animation.Keys)); |
151 |
} |
152 |
|
153 |
private void ExportGeometry(InstanceDescriptor descriptor, Dae.Scene scene, Motoko.GeometryDaeWriter geometryWriter) |
154 |
{ |
155 |
var animations = new List<Physics.ObjectAnimation>(animationNames.Count); |
156 |
|
157 |
foreach (string animationFilePath in animationNames) |
158 |
{ |
159 |
var file = InstanceFileManager.OpenFile(animationFilePath); |
160 |
|
161 |
if (file == null) |
162 |
{ |
163 |
Console.Error.WriteLine("Cannot find animation {0}", animationFilePath); |
164 |
continue; |
165 |
} |
166 |
|
167 |
animations.Add(Physics.ObjectDatReader.ReadAnimation(file.Descriptors[0])); |
168 |
} |
169 |
|
170 |
ExportGeometry(scene, geometryWriter, descriptor, animations); |
171 |
} |
172 |
|
173 |
private void ExportCharacterBodySet(InstanceDescriptor descriptor, Dae.Scene scene, Totoro.BodyDaeWriter bodyWriter) |
174 |
{ |
175 |
var body = Totoro.BodyDatReader.Read(descriptor); |
176 |
var node = bodyWriter.Write(body, noAnimation, null); |
177 |
|
178 |
scene.Nodes.Add(node); |
179 |
} |
180 |
|
181 |
private void ExportCharacterBody(InstanceDescriptor descriptor, Dae.Scene scene, Totoro.BodyDaeWriter bodyWriter) |
182 |
{ |
183 |
var animationName = animationNames.Count > 0 ? animationNames[0] : null; |
184 |
var characterClass = Game.CharacterClass.Read(descriptor, animationName); |
185 |
|
186 |
var body = Totoro.BodyDatReader.Read(characterClass.Body); |
187 |
var textures = characterClass.Textures; |
188 |
var pelvis = bodyWriter.Write(body, noAnimation, textures); |
189 |
|
190 |
scene.Nodes.Add(pelvis); |
191 |
|
192 |
var animation = noAnimation ? null : characterClass.Animation; |
193 |
|
194 |
if (animation != null) |
195 |
{ |
196 |
var anim = Totoro.AnimationDatReader.Read(animation); |
197 |
|
198 |
Totoro.AnimationDaeWriter.Write(pelvis, anim); |
199 |
} |
200 |
} |
201 |
|
202 |
private void ExportWeaponGeometry(InstanceDescriptor descriptor, Dae.Scene scene, Motoko.GeometryDaeWriter geometryWriter) |
203 |
{ |
204 |
var weaponClass = Game.WeaponClass.Read(descriptor); |
205 |
|
206 |
if (weaponClass.Geometry != null) |
207 |
ExportGeometry(weaponClass.Geometry, scene, geometryWriter); |
208 |
} |
209 |
|
210 |
private static void ExportGeometry(Dae.Scene scene, Motoko.GeometryDaeWriter geometryWriter, InstanceDescriptor m3gm, List<Physics.ObjectAnimation> animations) |
211 |
{ |
212 |
var geometry = Motoko.GeometryDatReader.Read(m3gm); |
213 |
|
214 |
if (animations != null && animations.Count > 0) |
215 |
{ |
216 |
geometry.HasTransform = true; |
217 |
geometry.Transform = Matrix.CreateScale(animations[0].Keys[0].Scale); |
218 |
} |
219 |
|
220 |
var node = geometryWriter.WriteNode(geometry, m3gm.Name); |
221 |
scene.Nodes.Add(node); |
222 |
|
223 |
if (animations != null && animations.Count > 0) |
224 |
{ |
225 |
var frames = new List<Physics.ObjectAnimationKey>(); |
226 |
int offset = 0; |
227 |
|
228 |
foreach (var animation in animations) |
229 |
{ |
230 |
foreach (var key in animation.Keys) |
231 |
{ |
232 |
frames.Add(new Physics.ObjectAnimationKey |
233 |
{ |
234 |
Translation = key.Translation, |
235 |
Rotation = key.Rotation, |
236 |
Time = key.Time + offset |
237 |
}); |
238 |
} |
239 |
|
240 |
offset += animation.Length; |
241 |
} |
242 |
|
243 |
ExportAnimation(node, frames); |
244 |
} |
245 |
} |
246 |
|
247 |
private static void ExportAnimation(Dae.Node node, List<Physics.ObjectAnimationKey> frames) |
248 |
{ |
249 |
var times = new float[frames.Count]; |
250 |
var interpolations = new string[times.Length]; |
251 |
var positions = new Vector3[frames.Count]; |
252 |
var angles = new Vector3[frames.Count]; |
253 |
|
254 |
for (int i = 0; i < times.Length; ++i) |
255 |
times[i] = frames[i].Time / 60.0f; |
256 |
|
257 |
for (int i = 0; i < interpolations.Length; i++) |
258 |
interpolations[i] = "LINEAR"; |
259 |
|
260 |
for (int i = 0; i < frames.Count; i++) |
261 |
positions[i] = frames[i].Translation; |
262 |
|
263 |
for (int i = 0; i < frames.Count; i++) |
264 |
angles[i] = frames[i].Rotation.ToEulerXYZ(); |
265 |
|
266 |
var translate = node.Transforms.Translate("translate", positions[0]); |
267 |
var rotateX = node.Transforms.Rotate("rotX", Vector3.UnitX, angles[0].X); |
268 |
var rotateY = node.Transforms.Rotate("rotY", Vector3.UnitY, angles[0].Y); |
269 |
var rotateZ = node.Transforms.Rotate("rotZ", Vector3.UnitZ, angles[0].Z); |
270 |
|
271 |
WriteSampler(times, interpolations, i => positions[i].X, translate, "X"); |
272 |
WriteSampler(times, interpolations, i => positions[i].Y, translate, "Y"); |
273 |
WriteSampler(times, interpolations, i => positions[i].Z, translate, "Z"); |
274 |
WriteSampler(times, interpolations, i => angles[i].X, rotateX, "ANGLE"); |
275 |
WriteSampler(times, interpolations, i => angles[i].Y, rotateY, "ANGLE"); |
276 |
WriteSampler(times, interpolations, i => angles[i].Z, rotateZ, "ANGLE"); |
277 |
} |
278 |
|
279 |
private static void WriteSampler(float[] times, string[] interpolations, Func<int, float> getValue, Dae.Transform transform, string targetName) |
280 |
{ |
281 |
var values = new float[times.Length]; |
282 |
|
283 |
for (int i = 0; i < values.Length; ++i) |
284 |
values[i] = getValue(i); |
285 |
|
286 |
transform.BindAnimation(targetName, new Dae.Sampler |
287 |
{ |
288 |
Inputs = { |
289 |
new Dae.Input(Dae.Semantic.Input, new Dae.Source(times, 1)), |
290 |
new Dae.Input(Dae.Semantic.Output, new Dae.Source(values, 1)), |
291 |
new Dae.Input(Dae.Semantic.Interpolation, new Dae.Source(interpolations, 1)) |
292 |
} |
293 |
}); |
294 |
} |
295 |
} |
296 |
} |