rev |
line source |
nuclear@54
|
1 /*
|
nuclear@54
|
2 goat3d - 3D scene, character, and animation file format library.
|
nuclear@54
|
3 Copyright (C) 2013-2014 John Tsiombikas <nuclear@member.fsf.org>
|
nuclear@54
|
4
|
nuclear@54
|
5 This program is free software: you can redistribute it and/or modify
|
nuclear@54
|
6 it under the terms of the GNU Lesser General Public License as published by
|
nuclear@54
|
7 the Free Software Foundation, either version 3 of the License, or
|
nuclear@54
|
8 (at your option) any later version.
|
nuclear@54
|
9
|
nuclear@54
|
10 This program is distributed in the hope that it will be useful,
|
nuclear@54
|
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
|
nuclear@54
|
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
nuclear@54
|
13 GNU Lesser General Public License for more details.
|
nuclear@54
|
14
|
nuclear@54
|
15 You should have received a copy of the GNU Lesser General Public License
|
nuclear@54
|
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
|
nuclear@54
|
17 */
|
nuclear@47
|
18 #include <list>
|
nuclear@14
|
19 #include <stdarg.h>
|
nuclear@14
|
20 #include "goat3d_impl.h"
|
nuclear@47
|
21 #include "anim/anim.h"
|
nuclear@17
|
22 #include "log.h"
|
nuclear@14
|
23
|
nuclear@47
|
24 using namespace g3dimpl;
|
nuclear@47
|
25
|
nuclear@14
|
26 static bool write_material(const Scene *scn, goat3d_io *io, const Material *mat, int level);
|
nuclear@14
|
27 static bool write_mesh(const Scene *scn, goat3d_io *io, const Mesh *mesh, int idx, int level);
|
nuclear@14
|
28 static bool write_light(const Scene *scn, goat3d_io *io, const Light *light, int level);
|
nuclear@14
|
29 static bool write_camera(const Scene *scn, goat3d_io *io, const Camera *cam, int level);
|
nuclear@14
|
30 static bool write_node(const Scene *scn, goat3d_io *io, const Node *node, int level);
|
nuclear@47
|
31 static bool write_node_anim(goat3d_io *io, const XFormNode *node, int level);
|
nuclear@14
|
32 static void xmlout(goat3d_io *io, int level, const char *fmt, ...);
|
nuclear@14
|
33
|
nuclear@14
|
34 bool Scene::savexml(goat3d_io *io) const
|
nuclear@14
|
35 {
|
nuclear@82
|
36 xmlout(io, 0, "<!-- vi:set filetype=xml: -->\n");
|
nuclear@82
|
37 xmlout(io, 0, "<!-- Goat3D XML scene description -->\n");
|
nuclear@14
|
38 xmlout(io, 0, "<scene>\n");
|
nuclear@14
|
39
|
nuclear@14
|
40 // write environment stuff
|
nuclear@14
|
41 xmlout(io, 1, "<env>\n");
|
nuclear@18
|
42 xmlout(io, 2, "<ambient float3=\"%g %g %g\"/>\n", ambient.x, ambient.y, ambient.z);
|
nuclear@18
|
43 xmlout(io, 1, "</env>\n\n");
|
nuclear@14
|
44
|
nuclear@14
|
45 for(size_t i=0; i<materials.size(); i++) {
|
nuclear@14
|
46 write_material(this, io, materials[i], 1);
|
nuclear@14
|
47 }
|
nuclear@14
|
48 for(size_t i=0; i<meshes.size(); i++) {
|
nuclear@58
|
49 write_mesh(this, io, meshes[i], (int)i, 1);
|
nuclear@14
|
50 }
|
nuclear@14
|
51 for(size_t i=0; i<lights.size(); i++) {
|
nuclear@14
|
52 write_light(this, io, lights[i], 1);
|
nuclear@14
|
53 }
|
nuclear@14
|
54 for(size_t i=0; i<cameras.size(); i++) {
|
nuclear@14
|
55 write_camera(this, io, cameras[i], 1);
|
nuclear@14
|
56 }
|
nuclear@14
|
57 for(size_t i=0; i<nodes.size(); i++) {
|
nuclear@14
|
58 write_node(this, io, nodes[i], 1);
|
nuclear@14
|
59 }
|
nuclear@14
|
60
|
nuclear@14
|
61 xmlout(io, 0, "</scene>\n");
|
nuclear@14
|
62 return true;
|
nuclear@14
|
63 }
|
nuclear@14
|
64
|
nuclear@55
|
65 bool Scene::save_anim_xml(goat3d_io *io) const
|
nuclear@47
|
66 {
|
nuclear@82
|
67 xmlout(io, 0, "<!-- vi:set filetype=xml: -->\n");
|
nuclear@82
|
68 xmlout(io, 0, "<!-- Goat3D XML animation -->\n");
|
nuclear@47
|
69 xmlout(io, 0, "<anim>\n");
|
nuclear@47
|
70
|
nuclear@55
|
71 if(!nodes.empty()) {
|
nuclear@55
|
72 const char *anim_name = nodes[0]->get_animation_name();
|
nuclear@55
|
73 if(anim_name && *anim_name) {
|
nuclear@55
|
74 xmlout(io, 1, "<name string=\"%s\"/>\n", anim_name);
|
nuclear@55
|
75 }
|
nuclear@47
|
76 }
|
nuclear@47
|
77
|
nuclear@55
|
78 for(size_t i=0; i<nodes.size(); i++) {
|
nuclear@55
|
79 write_node_anim(io, nodes[i], 1);
|
nuclear@47
|
80 }
|
nuclear@47
|
81
|
nuclear@47
|
82 xmlout(io, 0, "</anim>\n");
|
nuclear@47
|
83 return true;
|
nuclear@47
|
84 }
|
nuclear@47
|
85
|
nuclear@14
|
86 static bool write_material(const Scene *scn, goat3d_io *io, const Material *mat, int level)
|
nuclear@14
|
87 {
|
nuclear@14
|
88 xmlout(io, level, "<mtl>\n");
|
nuclear@14
|
89 xmlout(io, level + 1, "<name string=\"%s\"/>\n", mat->name.c_str());
|
nuclear@14
|
90
|
nuclear@14
|
91 for(int i=0; i<mat->get_attrib_count(); i++) {
|
nuclear@14
|
92 xmlout(io, level + 1, "<attr>\n");
|
nuclear@14
|
93 xmlout(io, level + 2, "<name string=\"%s\"/>\n", mat->get_attrib_name(i));
|
nuclear@14
|
94
|
nuclear@14
|
95 const MaterialAttrib &attr = (*mat)[i];
|
nuclear@18
|
96 xmlout(io, level + 2, "<val float4=\"%g %g %g %g\"/>\n", attr.value.x,
|
nuclear@14
|
97 attr.value.y, attr.value.z, attr.value.w);
|
nuclear@14
|
98 if(!attr.map.empty()) {
|
nuclear@14
|
99 xmlout(io, level + 2, "<map string=\"%s\"/>\n", attr.map.c_str());
|
nuclear@14
|
100 }
|
nuclear@14
|
101 xmlout(io, level + 1, "</attr>\n");
|
nuclear@14
|
102 }
|
nuclear@18
|
103 xmlout(io, level, "</mtl>\n\n");
|
nuclear@14
|
104 return true;
|
nuclear@14
|
105 }
|
nuclear@14
|
106
|
nuclear@14
|
107 static bool write_mesh(const Scene *scn, goat3d_io *io, const Mesh *mesh, int idx, int level)
|
nuclear@14
|
108 {
|
nuclear@14
|
109 // first write the external (openctm) mesh file
|
nuclear@14
|
110 const char *prefix = scn->get_name();
|
nuclear@14
|
111 if(!prefix) {
|
nuclear@14
|
112 prefix = "goat";
|
nuclear@14
|
113 }
|
nuclear@14
|
114
|
nuclear@14
|
115 char *mesh_filename = (char*)alloca(strlen(prefix) + 32);
|
nuclear@14
|
116 sprintf(mesh_filename, "%s-mesh%04d.ctm", prefix, idx);
|
nuclear@14
|
117
|
nuclear@19
|
118 if(!mesh->save(mesh_filename)) {
|
nuclear@19
|
119 return false;
|
nuclear@19
|
120 }
|
nuclear@14
|
121
|
nuclear@14
|
122 // then refer to that filename in the XML tags
|
nuclear@14
|
123 xmlout(io, level, "<mesh>\n");
|
nuclear@14
|
124 xmlout(io, level + 1, "<name string=\"%s\"/>\n", mesh->name.c_str());
|
nuclear@17
|
125 if(mesh->material) {
|
nuclear@17
|
126 xmlout(io, level + 1, "<material string=\"%s\"/>\n", mesh->material->name.c_str());
|
nuclear@17
|
127 }
|
nuclear@51
|
128 xmlout(io, level + 1, "<file string=\"%s\"/>\n", clean_filename(mesh_filename).c_str());
|
nuclear@18
|
129 xmlout(io, level, "</mesh>\n\n");
|
nuclear@14
|
130 return true;
|
nuclear@14
|
131 }
|
nuclear@14
|
132
|
nuclear@14
|
133 static bool write_light(const Scene *scn, goat3d_io *io, const Light *light, int level)
|
nuclear@14
|
134 {
|
nuclear@14
|
135 return true;
|
nuclear@14
|
136 }
|
nuclear@14
|
137
|
nuclear@14
|
138 static bool write_camera(const Scene *scn, goat3d_io *io, const Camera *cam, int level)
|
nuclear@14
|
139 {
|
nuclear@14
|
140 return true;
|
nuclear@14
|
141 }
|
nuclear@14
|
142
|
nuclear@14
|
143 static bool write_node(const Scene *scn, goat3d_io *io, const Node *node, int level)
|
nuclear@14
|
144 {
|
nuclear@30
|
145 xmlout(io, level, "<node>\n");
|
nuclear@30
|
146 xmlout(io, level + 1, "<name string=\"%s\"/>\n", node->get_name());
|
nuclear@30
|
147
|
nuclear@47
|
148 const XFormNode *parent = node->get_parent();
|
nuclear@30
|
149 if(parent) {
|
nuclear@30
|
150 xmlout(io, level + 1, "<parent string=\"%s\"/>\n", parent->get_name());
|
nuclear@30
|
151 }
|
nuclear@30
|
152
|
nuclear@30
|
153 const char *type = 0;
|
nuclear@30
|
154 const Object *obj = node->get_object();
|
nuclear@30
|
155 if(dynamic_cast<const Mesh*>(obj)) {
|
nuclear@30
|
156 type = "mesh";
|
nuclear@30
|
157 } else if(dynamic_cast<const Light*>(obj)) {
|
nuclear@30
|
158 type = "light";
|
nuclear@30
|
159 } else if(dynamic_cast<const Camera*>(obj)) {
|
nuclear@30
|
160 type = "camera";
|
nuclear@30
|
161 }
|
nuclear@30
|
162
|
nuclear@30
|
163 if(type) {
|
nuclear@30
|
164 xmlout(io, level + 1, "<%s string=\"%s\"/>\n", type, obj->name.c_str());
|
nuclear@30
|
165 }
|
nuclear@30
|
166
|
nuclear@30
|
167 Vector3 pos = node->get_node_position();
|
nuclear@30
|
168 Quaternion rot = node->get_node_rotation();
|
nuclear@30
|
169 Vector3 scale = node->get_node_scaling();
|
nuclear@30
|
170 Vector3 pivot = node->get_pivot();
|
nuclear@30
|
171
|
nuclear@30
|
172 Matrix4x4 xform;
|
nuclear@30
|
173 node->get_node_xform(0, &xform);
|
nuclear@30
|
174
|
nuclear@30
|
175 xmlout(io, level + 1, "<pos float3=\"%g %g %g\"/>\n", pos.x, pos.y, pos.z);
|
nuclear@30
|
176 xmlout(io, level + 1, "<rot float4=\"%g %g %g %g\"/>\n", rot.v.x, rot.v.y, rot.v.z, rot.s);
|
nuclear@30
|
177 xmlout(io, level + 1, "<scale float3=\"%g %g %g\"/>\n", scale.x, scale.y, scale.z);
|
nuclear@30
|
178 xmlout(io, level + 1, "<pivot float3=\"%g %g %g\"/>\n", pivot.x, pivot.y, pivot.z);
|
nuclear@30
|
179
|
nuclear@30
|
180 xmlout(io, level + 1, "<matrix0 float4=\"%g %g %g %g\"/>\n", xform[0][0], xform[0][1], xform[0][2], xform[0][3]);
|
nuclear@30
|
181 xmlout(io, level + 1, "<matrix1 float4=\"%g %g %g %g\"/>\n", xform[1][0], xform[1][1], xform[1][2], xform[1][3]);
|
nuclear@30
|
182 xmlout(io, level + 1, "<matrix2 float4=\"%g %g %g %g\"/>\n", xform[2][0], xform[2][1], xform[2][2], xform[2][3]);
|
nuclear@30
|
183
|
nuclear@30
|
184 xmlout(io, level, "</node>\n");
|
nuclear@14
|
185 return true;
|
nuclear@14
|
186 }
|
nuclear@14
|
187
|
nuclear@47
|
188 static bool write_node_anim(goat3d_io *io, const XFormNode *node, int level)
|
nuclear@47
|
189 {
|
nuclear@51
|
190 /* NOTE: the order of names must correspond to the order of the
|
nuclear@48
|
191 * XFormNode::POSITION_TRACK/ROTATION_TRACK/SCALING_TRACK enum
|
nuclear@48
|
192 */
|
nuclear@47
|
193 static const char *attr_names[] = { "position", "rotation", "scaling" };
|
nuclear@47
|
194
|
nuclear@48
|
195 // for each of: position/rotation/scaling
|
nuclear@48
|
196 for(int i=0; i<3; i++) {
|
nuclear@48
|
197 int num_keys = node->get_key_count(i);
|
nuclear@48
|
198 if(!num_keys) continue;
|
nuclear@47
|
199
|
nuclear@47
|
200 xmlout(io, level + 1, "<track>\n");
|
nuclear@47
|
201 xmlout(io, level + 2, "<node string=\"%s\"/>\n", node->get_name());
|
nuclear@48
|
202 xmlout(io, level + 2, "<attr string=\"%s\"/>\n\n", attr_names[i]);
|
nuclear@47
|
203
|
nuclear@48
|
204 // for each key in that track
|
nuclear@48
|
205 for(int j=0; j<num_keys; j++) {
|
nuclear@48
|
206 long tm = node->get_key_time(i, j);
|
nuclear@48
|
207
|
nuclear@48
|
208 float value[4];
|
nuclear@48
|
209 int num_elems = node->get_key_value(i, j, value);
|
nuclear@48
|
210
|
nuclear@48
|
211 if(num_elems == 3) {
|
nuclear@48
|
212 xmlout(io, level + 2, "<key><time int=\"%ld\"/><value float3=\"%g %g %g\"/></key>\n",
|
nuclear@48
|
213 tm, value[0], value[1], value[2]);
|
nuclear@48
|
214 } else {
|
nuclear@48
|
215 xmlout(io, level + 2, "<key><time int=\"%ld\"/><value float4=\"%g %g %g %g\"/></key>\n",
|
nuclear@48
|
216 tm, value[0], value[1], value[2], value[3]);
|
nuclear@48
|
217 }
|
nuclear@48
|
218 }
|
nuclear@47
|
219
|
nuclear@47
|
220 xmlout(io, level + 1, "</track>\n");
|
nuclear@47
|
221 }
|
nuclear@47
|
222 return true;
|
nuclear@47
|
223 }
|
nuclear@14
|
224
|
nuclear@14
|
225 static void xmlout(goat3d_io *io, int level, const char *fmt, ...)
|
nuclear@14
|
226 {
|
nuclear@14
|
227 for(int i=0; i<level; i++) {
|
nuclear@14
|
228 io_fprintf(io, " ");
|
nuclear@14
|
229 }
|
nuclear@14
|
230
|
nuclear@14
|
231 va_list ap;
|
nuclear@14
|
232 va_start(ap, fmt);
|
nuclear@14
|
233 io_vfprintf(io, fmt, ap);
|
nuclear@14
|
234 va_end(ap);
|
nuclear@14
|
235 }
|