fixed: [#34080] import of COLLADA breaks on '#' in filepath
[blender.git] / source / blender / collada / collada_utils.cpp
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * Contributor(s): Chingiz Dyussenov, Arystanbek Dyussenov, Nathan Letwory.
19  *
20  * ***** END GPL LICENSE BLOCK *****
21  */
22
23 /** \file blender/collada/collada_utils.cpp
24  *  \ingroup collada
25  */
26
27
28 /* COLLADABU_ASSERT, may be able to remove later */
29 #include "COLLADABUPlatform.h"
30
31 #include "COLLADAFWGeometry.h"
32 #include "COLLADAFWMeshPrimitive.h"
33 #include "COLLADAFWMeshVertexData.h"
34
35 #include "collada_utils.h"
36
37 extern "C" {
38 #include "DNA_modifier_types.h"
39 #include "DNA_customdata_types.h"
40 #include "DNA_object_types.h"
41 #include "DNA_mesh_types.h"
42 #include "DNA_scene_types.h"
43 #include "DNA_armature_types.h"
44
45 #include "BLI_math.h"
46 #include "BLI_linklist.h"
47
48 #include "BKE_context.h"
49 #include "BKE_customdata.h"
50 #include "BKE_depsgraph.h"
51 #include "BKE_object.h"
52 #include "BKE_global.h"
53 #include "BKE_mesh.h"
54 #include "BKE_scene.h"
55 #include "BKE_DerivedMesh.h"
56
57 #include "WM_api.h" // XXX hrm, see if we can do without this
58 #include "WM_types.h"
59 }
60
61 float bc_get_float_value(const COLLADAFW::FloatOrDoubleArray& array, unsigned int index)
62 {
63         if (index >= array.getValuesCount())
64                 return 0.0f;
65
66         if (array.getType() == COLLADAFW::MeshVertexData::DATA_TYPE_FLOAT)
67                 return array.getFloatValues()->getData()[index];
68         else 
69                 return array.getDoubleValues()->getData()[index];
70 }
71
72 // copied from /editors/object/object_relations.c
73 int bc_test_parent_loop(Object *par, Object *ob)
74 {
75         /* test if 'ob' is a parent somewhere in par's parents */
76         
77         if (par == NULL) return 0;
78         if (ob == par) return 1;
79         
80         return bc_test_parent_loop(par->parent, ob);
81 }
82
83 // a shortened version of parent_set_exec()
84 // if is_parent_space is true then ob->obmat will be multiplied by par->obmat before parenting
85 int bc_set_parent(Object *ob, Object *par, bContext *C, bool is_parent_space)
86 {
87         Object workob;
88         Main *bmain = CTX_data_main(C);
89         Scene *sce = CTX_data_scene(C);
90         
91         if (!par || bc_test_parent_loop(par, ob))
92                 return false;
93
94         ob->parent = par;
95         ob->partype = PAROBJECT;
96
97         ob->parsubstr[0] = 0;
98
99         if (is_parent_space) {
100                 float mat[4][4];
101                 // calc par->obmat
102                 BKE_object_where_is_calc(sce, par);
103
104                 // move child obmat into world space
105                 mult_m4_m4m4(mat, par->obmat, ob->obmat);
106                 copy_m4_m4(ob->obmat, mat);
107         }
108         
109         // apply child obmat (i.e. decompose it into rot/loc/size)
110         BKE_object_apply_mat4(ob, ob->obmat, 0, 0);
111
112         // compute parentinv
113         BKE_object_workob_calc_parent(sce, ob, &workob);
114         invert_m4_m4(ob->parentinv, workob.obmat);
115
116         ob->recalc |= OB_RECALC_OB | OB_RECALC_DATA;
117         par->recalc |= OB_RECALC_OB;
118
119         /** done once after import
120         DAG_scene_sort(bmain, sce);
121         DAG_ids_flush_update(bmain, 0);
122         WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
123     */
124
125
126         return true;
127 }
128
129 Object *bc_add_object(Scene *scene, int type, const char *name)
130 {
131         Object *ob = BKE_object_add_only_object(G.main, type, name);
132
133         ob->data = BKE_object_obdata_add_from_type(type);
134         ob->lay = scene->lay;
135         ob->recalc |= OB_RECALC_OB | OB_RECALC_DATA | OB_RECALC_TIME;
136
137         BKE_scene_base_select(scene, BKE_scene_base_add(scene, ob));
138
139         return ob;
140 }
141
142 Mesh *bc_to_mesh_apply_modifiers(Scene *scene, Object *ob, BC_export_mesh_type export_mesh_type)
143 {
144         Mesh *tmpmesh;
145         CustomDataMask mask = CD_MASK_MESH;
146         DerivedMesh *dm = NULL;
147         switch (export_mesh_type) {
148                 case BC_MESH_TYPE_VIEW: {
149                         dm = mesh_create_derived_view(scene, ob, mask);
150                         break;
151                 }
152                 case BC_MESH_TYPE_RENDER: {
153                         dm = mesh_create_derived_render(scene, ob, mask);
154                         break;
155                 }
156         }
157
158         tmpmesh = BKE_mesh_add(G.main, "ColladaMesh"); // name is not important here
159         DM_to_mesh(dm, tmpmesh, ob);
160         dm->release(dm);
161         return tmpmesh;
162 }
163
164 Object *bc_get_assigned_armature(Object *ob)
165 {
166         Object *ob_arm = NULL;
167
168         if (ob->parent && ob->partype == PARSKEL && ob->parent->type == OB_ARMATURE) {
169                 ob_arm = ob->parent;
170         }
171         else {
172                 ModifierData *mod;
173                 for (mod = (ModifierData *)ob->modifiers.first; mod; mod = mod->next) {
174                         if (mod->type == eModifierType_Armature) {
175                                 ob_arm = ((ArmatureModifierData *)mod)->object;
176                         }
177                 }
178         }
179
180         return ob_arm;
181 }
182
183 // Returns the highest selected ancestor
184 // returns NULL if no ancestor is selected
185 // IMPORTANT: This function expects that
186 // all exported objects have set:
187 // ob->id.flag & LIB_DOIT
188 Object *bc_get_highest_selected_ancestor_or_self(LinkNode *export_set, Object *ob) 
189 {
190         Object *ancestor = ob;
191         while (ob->parent && bc_is_marked(ob->parent)) {
192                 ob = ob->parent;
193                 ancestor = ob;
194         }
195         return ancestor;
196 }
197
198
199 bool bc_is_base_node(LinkNode *export_set, Object *ob)
200 {
201         Object *root = bc_get_highest_selected_ancestor_or_self(export_set, ob);
202         return (root == ob);
203 }
204
205 bool bc_is_in_Export_set(LinkNode *export_set, Object *ob)
206 {
207         return (BLI_linklist_index(export_set, ob) != -1);
208 }
209
210 bool bc_has_object_type(LinkNode *export_set, short obtype)
211 {
212         LinkNode *node;
213         
214         for (node = export_set; node; node = node->next) {
215                 Object *ob = (Object *)node->link;
216                 /* XXX - why is this checking for ob->data? - we could be looking for empties */
217                 if (ob->type == obtype && ob->data) {
218                         return true;
219                 }
220         }
221         return false;
222 }
223
224 int bc_is_marked(Object *ob)
225 {
226         return ob && (ob->id.flag & LIB_DOIT);
227 }
228
229 void bc_remove_mark(Object *ob)
230 {
231         ob->id.flag &= ~LIB_DOIT;
232 }
233
234 void bc_set_mark(Object *ob)
235 {
236         ob->id.flag |= LIB_DOIT;
237 }
238
239 // Use bubble sort algorithm for sorting the export set
240 void bc_bubble_sort_by_Object_name(LinkNode *export_set)
241 {
242         bool sorted = false;
243         LinkNode *node;
244         for (node = export_set; node->next && !sorted; node = node->next) {
245
246                 sorted = true;
247                 
248                 LinkNode *current;
249                 for (current = export_set; current->next; current = current->next) {
250                         Object *a = (Object *)current->link;
251                         Object *b = (Object *)current->next->link;
252
253                         if (strcmp(a->id.name, b->id.name) > 0) {
254                                 current->link       = b;
255                                 current->next->link = a;
256                                 sorted = false;
257                         }
258                         
259                 }
260         }
261 }
262
263 /* Check if a bone is the top most exportable bone in the bone hierarchy. 
264  * When deform_bones_only == false, then only bones with NO parent 
265  * can be root bones. Otherwise the top most deform bones in the hierarchy
266  * are root bones.
267  */
268 bool bc_is_root_bone(Bone *aBone, bool deform_bones_only)
269 {
270         if (deform_bones_only) {
271                 Bone *root = NULL;
272                 Bone *bone = aBone;
273                 while (bone) {
274                         if (!(bone->flag & BONE_NO_DEFORM))
275                                 root = bone;
276                         bone = bone->parent;
277                 }
278                 return (aBone == root);
279         }
280         else
281                 return !(aBone->parent);
282 }
283
284 int bc_get_active_UVLayer(Object *ob)
285 {
286         Mesh *me = (Mesh *)ob->data;
287         return CustomData_get_active_layer_index(&me->fdata, CD_MTFACE);
288 }
289
290 std::string bc_url_encode(std::string data) {
291         /* XXX We probably do not need to do a full encoding.
292            But in case that is necessary,then it can be added here.
293         */
294         return bc_replace_string(data,"#", "%23");
295 }
296
297 std::string bc_replace_string(std::string data, const std::string& pattern,
298                           const std::string& replacement) {
299     size_t pos = 0;
300     while((pos = data.find(pattern, pos)) != std::string::npos) {
301          data.replace(pos, pattern.length(), replacement);
302          pos += replacement.length();
303     }
304     return data;
305 }