part 1 of cleaning up my little array macro library to be a formal API. also removed...
[blender.git] / source / blender / blenkernel / intern / verse_object_node.c
1 /**
2  * $Id: verse_object_node.c 12931 2007-12-17 18:20:48Z theeth $
3  *
4  * ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version. The Blender
10  * Foundation also sells licenses for use in proprietary software under
11  * the Blender License.  See http://www.blender.org/BL/ for information
12  * about this.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22  *
23  * Contributor(s): Jiri Hnidek.
24  *
25  * ***** END GPL/BL DUAL LICENSE BLOCK *****
26  */
27
28 #ifdef WITH_VERSE
29
30 #include <string.h>
31 #include <math.h>
32
33 #include "MEM_guardedalloc.h"
34
35 #include "DNA_listBase.h"
36 #include "DNA_userdef_types.h"
37
38 #include "BLI_dynamiclist.h"
39 #include "BLI_blenlib.h"
40 #include "BLI_arithb.h"
41
42 #include "BIF_verse.h"
43
44 #include "BKE_verse.h"
45 #include "BKE_utildefines.h"
46
47 #include "verse.h"
48
49 /* function prototypes of static functions */
50
51 /* callback functions */
52 static void cb_o_transform_pos_real32(void *user_data, VNodeID node_id, uint32 time_s, uint32 time_f, const real32 *pos, const real32 *speed, const real32 *accelerate, const real32 *drag_normal, real32 drag);
53 static void cb_o_transform_rot_real32(void *user_data, VNodeID node_id, uint32 time_s, uint32 time_f, const VNQuat32 *temp, const VNQuat32 *speed, const VNQuat32 *accelerate, const VNQuat32 *drag_normal, real32 drag);
54 static void cb_o_transform_scale_real32(void *user_data, VNodeID node_id, real32 scale_x, real32 scale_y, real32 scale_z);
55 static void cb_o_link_set(void *user_data, VNodeID node_id, uint16 link_id, VNodeID link, const char *label, uint32 target_id);
56 static void cb_o_link_destroy(void *user_data, VNodeID node_id,uint16 link_id);
57
58 /* other functions */
59 static void set_target_node_link_pointer(struct VNode *vnode, struct VLink *vlink);
60 static void free_verse_link_data(struct VLink *vlink);
61
62 /*
63  * find noy sent VerseLink in queue
64  */
65 VLink *find_unsent_child_vlink(VerseSession *session, VNode *vnode)
66 {
67         struct VLink *vlink;
68
69         if(vnode->type!=V_NT_OBJECT) return NULL;
70
71         vlink = ((VObjectData*)vnode->data)->queue.first;
72         while(vlink) {
73                 if(vlink->target->id != -1) {
74                         printf("\t vlink found, vnode target id %d\n", vlink->target->id);
75                         return vlink;
76                 }
77                 vlink = vlink->next;
78         }
79         return NULL;
80 }
81
82 /*
83  * find unsent VerseLink "pointing at this VerseNode"
84  */
85 VLink *find_unsent_parent_vlink(VerseSession *session, VNode *vnode)
86 {
87         struct VNode *tmp;
88         struct VLink *vlink;
89
90         tmp = session->nodes.lb.first;
91
92         while(tmp) {
93                 if(tmp->type==V_NT_OBJECT) {
94                         vlink = ((VObjectData*)tmp->data)->queue.first;
95                         while(vlink) {
96                                 if(vlink->target == vnode)
97                                         return vlink;
98                                 vlink = vlink->next;
99                         }
100                 }
101                 tmp = tmp->next;
102         }
103         return NULL;
104 }
105
106 /*
107  * send object position to verse server
108  */
109 void send_verse_object_position(VNode *vnode)
110 {
111         float tmp;
112         
113         ((VObjectData*)vnode->data)->flag &= ~POS_SEND_READY;
114
115         /* we have to do rotation around x axis (+pi/2) to be
116            compatible with other verse applications */
117         tmp = -((VObjectData*)vnode->data)->pos[1];
118         ((VObjectData*)vnode->data)->pos[1] = ((VObjectData*)vnode->data)->pos[2];
119         ((VObjectData*)vnode->data)->pos[2] = tmp;
120
121         verse_send_o_transform_pos_real32(
122                         vnode->id,      /* node id */
123                         0,              /* time_s ... no interpolation */
124                         0,              /* time_f ... no interpolation */
125                         ((VObjectData*)vnode->data)->pos,
126                         NULL,           /* speed ... no interpolation */
127                         NULL,           /* accelerate  ... no interpolation */
128                         NULL,           /* drag normal ... no interpolation */
129                         0.0);           /* drag ... no interpolation */
130 }
131
132 /*
133  * send object rotation to verse server
134  */
135 void send_verse_object_rotation(VNode *vnode)
136 {
137         VNQuat32 quat;
138         float q[4] = {cos(-M_PI/4), -sin(-M_PI/4), 0, 0}, v[4], tmp[4];
139
140         /* inverse transformation to transformation in function cb_o_transform_rot_real32 */
141         QuatMul(v, ((VObjectData*)vnode->data)->quat, q);
142         q[1]= sin(-M_PI/4);
143         QuatMul(tmp, q, v);
144
145         quat.x = tmp[1];
146         quat.y = tmp[2];
147         quat.z = tmp[3];
148         quat.w = tmp[0];
149         
150         ((VObjectData*)vnode->data)->flag &= ~ROT_SEND_READY;
151
152         verse_send_o_transform_rot_real32(
153                         vnode->id,      /* node id */
154                         0,              /* time_s ... no interpolation */
155                         0,              /* time_f ... no interpolation */
156                         &quat,
157                         NULL,           /* speed ... no interpolation */
158                         NULL,           /* accelerate  ... no interpolation */
159                         NULL,           /* drag normal ... no interpolation */
160                         0.0);           /* drag ... no interpolation */
161 }
162
163 /*
164  * send object rotation to verse server 
165  */
166 void send_verse_object_scale(VNode *vnode)
167 {
168         float tmp;
169
170         ((VObjectData*)vnode->data)->flag &= ~SCALE_SEND_READY;
171
172         /* we have to do rotation around x axis (+pi/2) to be
173            compatible with other verse applications */
174         tmp = ((VObjectData*)vnode->data)->scale[1];
175         ((VObjectData*)vnode->data)->scale[1] = ((VObjectData*)vnode->data)->scale[2];
176         ((VObjectData*)vnode->data)->scale[2] = tmp;
177
178         verse_send_o_transform_scale_real32(
179                         vnode->id,
180                         ((VObjectData*)vnode->data)->scale[0],
181                         ((VObjectData*)vnode->data)->scale[1],
182                         ((VObjectData*)vnode->data)->scale[2]);
183 }
184
185 /*
186  * send VerseLink to verse server
187  */
188 void send_verse_link(VLink *vlink)
189 {
190         verse_session_set(vlink->session->vsession);
191
192         verse_send_o_link_set(
193                         vlink->source->id,
194                         vlink->id,
195                         vlink->target->id,
196                         vlink->label,
197                         vlink->target_id);
198 }
199
200 /*
201  * set up pointer at VerseLink of target node (geometry node, material node, etc.)
202  */
203 static void set_target_node_link_pointer(VNode *vnode, VLink *vlink)
204 {
205         switch (vnode->type) {
206                 case V_NT_GEOMETRY:
207                         ((VGeomData*)vnode->data)->vlink = vlink;
208                         break;
209                 default:
210                         break;
211         }
212 }
213
214 /*
215  * free VerseLink and it's label
216  */
217 static void free_verse_link_data(VLink *vlink)
218 {
219         MEM_freeN(vlink->label);
220 }
221
222 /*
223  * create new VerseLink
224  */
225 VLink *create_verse_link(
226                 VerseSession *session,
227                 VNode *source,
228                 VNode *target,
229                 uint16 link_id,
230                 uint32 target_id,
231                 const char *label)
232 {
233         struct VLink *vlink;
234
235         vlink = (VLink*)MEM_mallocN(sizeof(VLink), "VerseLink");
236         vlink->session = session;
237         vlink->source = source;
238         vlink->target = target;
239         vlink->id = link_id;
240         vlink->target_id = target_id;
241
242         set_target_node_link_pointer(target, vlink);
243
244         vlink->label = (char*)MEM_mallocN(sizeof(char)*(strlen(label)+1), "VerseLink label");
245         vlink->label[0] = '\0';
246         strcat(vlink->label, label);
247
248         vlink->flag = 0;
249
250         vlink->post_link_set = post_link_set;
251         vlink->post_link_destroy = post_link_destroy;
252
253         return vlink;
254 }
255
256 /*
257  * free ObjectData (links, links in queue and lables of links)
258  */
259 void free_object_data(VNode *vnode)
260 {
261         struct VerseSession *session = vnode->session;
262         struct VObjectData *obj = (VObjectData*)vnode->data;
263         struct VLink *vlink;
264         struct VMethodGroup *vmg;
265
266         if(!obj) return;
267
268         /* free all labels of links in dlist */
269         vlink = obj->links.lb.first;
270         while(vlink){
271                 free_verse_link_data(vlink);
272                 vlink = vlink->next;
273         }
274
275         /* free all labels of links waiting in queue */
276         vlink = obj->queue.first;
277         while(vlink){
278                 free_verse_link_data(vlink);
279                 vlink = vlink->next;
280         }
281         /* free dynamic list and sendig queue of links */
282         BLI_dlist_destroy(&(obj->links));
283         BLI_freelistN(&(obj->queue));
284         
285         /* free method groups and their methods */
286         for(vmg = vnode->methodgroups.first; vmg; vmg= vmg->next) {
287                 free_verse_methodgroup(vmg);
288         }
289         BLI_freelistN(&(vnode->methodgroups));
290
291         /* free constraint between VerseNode and Object */
292         obj->post_object_free_constraint(vnode);
293
294         /* unsubscribe from receiving changes of transformation matrix */
295         if(session->flag & VERSE_CONNECTED)
296                 verse_send_o_transform_unsubscribe(vnode->id, 0);
297 }
298
299 /*
300  * create new object data
301  */
302 VObjectData *create_object_data(void)
303 {
304         VObjectData *obj;
305
306         obj = (VObjectData*)MEM_mallocN(sizeof(VObjectData), "VerseObjectData");
307         obj->object = NULL;
308         BLI_dlist_init(&(obj->links));
309         obj->queue.first = obj->queue.last = NULL;
310         obj->flag = 0;
311
312         /* transformation matrix */
313         obj->pos[0] = obj->pos[1] = obj->pos[2] = 0.0;
314         obj->quat[0] = obj->quat[1] = obj->quat[2] = 0.0; obj->quat[3] = 1;
315         obj->scale[0] = obj->scale[1] = obj->scale[2] = 1.0;
316
317         /* transformation flags */
318         obj->flag |= POS_SEND_READY;
319         obj->flag |= ROT_SEND_READY;
320         obj->flag |= SCALE_SEND_READY;
321
322         /* set up pointers at post callback functions */
323 /*      obj->post_transform = post_transform;*/
324         obj->post_transform_pos = post_transform_pos;
325         obj->post_transform_rot = post_transform_rot;
326         obj->post_transform_scale = post_transform_scale;
327         obj->post_object_free_constraint = post_object_free_constraint;
328
329         return obj;
330 }
331
332 /*
333  * callback function: 
334  */
335 static void cb_o_transform_pos_real32(
336                 void *user_data,
337                 VNodeID node_id,
338                 uint32 time_s,
339                 uint32 time_f,
340                 const real32 *pos,
341                 const real32 *speed,
342                 const real32 *accelerate,
343                 const real32 *drag_normal,
344                 real32 drag)
345 {
346         struct VerseSession *session = (VerseSession*)current_verse_session();
347         struct VNode *vnode;
348         float vec[3], dt, tmp;
349
350         if(!session) return;
351
352         vnode = BLI_dlist_find_link(&(session->nodes), (unsigned int)node_id);
353
354         ((VObjectData*)vnode->data)->flag |= POS_SEND_READY;
355
356         /* verse server sends automaticaly some stupid default values ...
357          * we have to ignore these values, when we created this object node */
358         if( (vnode->owner_id==VN_OWNER_MINE) && !(((VObjectData*)vnode->data)->flag & POS_RECEIVE_READY) ) {
359                 ((VObjectData*)vnode->data)->flag |= POS_RECEIVE_READY;
360                 return;
361         }
362
363         dt = time_s + time_f/(0xffff);
364
365         if(pos) {
366                 vec[0] = pos[0];
367                 vec[1] = pos[1];
368                 vec[2] = pos[2];
369         }
370         else {
371                 vec[0] = 0.0f;
372                 vec[1] = 0.0f;
373                 vec[2] = 0.0f;
374         }
375
376         if(speed) {
377                 vec[0] += speed[0]*dt;
378                 vec[1] += speed[1]*dt;
379                 vec[2] += speed[2]*dt;
380         }
381
382         if(accelerate) {
383                 vec[0] += accelerate[0]*dt*dt/2;
384                 vec[1] += accelerate[1]*dt*dt/2;
385                 vec[2] += accelerate[2]*dt*dt/2;
386         }
387
388         /* we have to do rotation around x axis (+pi/2) to be
389            compatible with other verse applications */
390         tmp = vec[1];
391         vec[1] = -vec[2];
392         vec[2] = tmp;
393
394         if( (((VObjectData*)vnode->data)->pos[0] != vec[0]) ||
395                         (((VObjectData*)vnode->data)->pos[1] != vec[1]) ||
396                         (((VObjectData*)vnode->data)->pos[2] != vec[2]))
397         {
398                 ((VObjectData*)vnode->data)->pos[0] = vec[0];
399                 ((VObjectData*)vnode->data)->pos[1] = vec[1];
400                 ((VObjectData*)vnode->data)->pos[2] = vec[2];
401
402                 ((VObjectData*)vnode->data)->post_transform_pos(vnode);
403         }
404 }
405
406 /*
407  * callback function:
408  */
409 static void cb_o_transform_rot_real32(
410                 void *user_data,
411                 VNodeID node_id,
412                 uint32 time_s,
413                 uint32 time_f,
414                 const VNQuat32 *quat,
415                 const VNQuat32 *speed,
416                 const VNQuat32 *accelerate,
417                 const VNQuat32 *drag_normal,
418                 real32 drag)
419 {
420         struct VerseSession *session = (VerseSession*)current_verse_session();
421         struct VNode *vnode;
422         float temp[4]={0, 0, 0, 0}, v[4], dt;           /* temporary quaternions */
423         float q[4]={cos(M_PI/4), -sin(M_PI/4), 0, 0};   /* conjugate quaternion (represents rotation
424                                                            around x-axis +90 degrees) */
425
426         if(!session) return;
427
428         vnode = BLI_dlist_find_link(&(session->nodes), (unsigned int)node_id);
429
430         ((VObjectData*)vnode->data)->flag |= ROT_SEND_READY;
431         
432         /* verse server sends automaticaly some stupid default values ...
433          * we have to ignore these values, when we created this object node */
434         if( (vnode->owner_id==VN_OWNER_MINE) && !(((VObjectData*)vnode->data)->flag & ROT_RECEIVE_READY) ) {
435                 ((VObjectData*)vnode->data)->flag |= ROT_RECEIVE_READY;
436                 return;
437         }
438
439         dt = time_s + time_f/(0xffff);
440
441         if(quat) {
442                 temp[1] = quat->x;
443                 temp[2] = quat->y;
444                 temp[3] = quat->z;
445                 temp[0] = quat->w;
446         }
447
448         if(speed) {
449                 temp[1] += speed->x*dt;
450                 temp[2] += speed->y*dt;
451                 temp[3] += speed->z*dt;
452                 temp[0] += speed->w*dt;
453         }
454
455         if(accelerate) {
456                 temp[1] += accelerate->x*dt*dt/2;
457                 temp[2] += accelerate->y*dt*dt/2;
458                 temp[3] += accelerate->z*dt*dt/2;
459                 temp[0] += accelerate->w*dt*dt/2;
460         }
461
462         /* following matematical operation transform rotation:
463          *
464          * v' = quaternion * v * conjugate_quaternion
465          *
466          *, where v is original representation of rotation */
467
468         QuatMul(v, temp, q);
469         q[1]= sin(M_PI/4);      /* normal quaternion */
470         QuatMul(temp, q, v);
471
472         if( (((VObjectData*)vnode->data)->quat[0] != temp[0]) ||
473                         (((VObjectData*)vnode->data)->quat[1] != temp[1]) ||
474                         (((VObjectData*)vnode->data)->quat[2] != temp[2]) ||
475                         (((VObjectData*)vnode->data)->quat[3] != temp[3]))
476         {
477                 QUATCOPY(((VObjectData*)vnode->data)->quat, temp);
478
479                 ((VObjectData*)vnode->data)->post_transform_rot(vnode);
480         }
481 }
482
483 /*
484  * callback function:
485  */
486 static void cb_o_transform_scale_real32(
487                 void *user_data,
488                 VNodeID node_id,
489                 real32 scale_x,
490                 real32 scale_y,
491                 real32 scale_z)
492 {
493         struct VerseSession *session = (VerseSession*)current_verse_session();
494         struct VNode *vnode;
495         real32 tmp;
496
497         if(!session) return;
498
499         vnode = BLI_dlist_find_link(&(session->nodes), (unsigned int)node_id);
500
501         ((VObjectData*)vnode->data)->flag |= SCALE_SEND_READY;
502         
503         /* verse server sends automaticaly some stupid default values ...
504          * we have to ignore these values, when we created this object node */
505         if( (vnode->owner_id==VN_OWNER_MINE) && !(((VObjectData*)vnode->data)->flag & SCALE_RECEIVE_READY) ) {
506                 ((VObjectData*)vnode->data)->flag |= SCALE_RECEIVE_READY;
507                 return;
508         }
509
510         /* flip axis (verse spec) */
511         tmp = scale_y;
512         scale_y = scale_z;
513         scale_z = tmp;
514
515         /* z and y axis are flipped here too */
516         if( (((VObjectData*)vnode->data)->scale[0] != scale_x) ||
517                         (((VObjectData*)vnode->data)->scale[1] != scale_y) ||
518                         (((VObjectData*)vnode->data)->scale[2] != scale_z))
519         {
520                 ((VObjectData*)vnode->data)->scale[0] = scale_x;
521                 ((VObjectData*)vnode->data)->scale[1] = scale_y;
522                 ((VObjectData*)vnode->data)->scale[2] = scale_z;
523
524                 ((VObjectData*)vnode->data)->post_transform_scale(vnode);
525         }
526 }
527
528 /*
529  * callback function: link between object node and some other node was created
530  */
531 static void cb_o_link_set(
532                 void *user_data,
533                 VNodeID node_id,
534                 uint16 link_id,
535                 VNodeID link,
536                 const char *label,
537                 uint32 target_id)
538 {
539         struct VLink *vlink;
540         struct VNode *source;
541         struct VNode *target;
542
543         struct VerseSession *session = (VerseSession*)current_verse_session();
544
545         if(!session) return;
546
547         source = BLI_dlist_find_link(&(session->nodes), (unsigned int)node_id);
548         target = BLI_dlist_find_link(&(session->nodes), (unsigned int)link);
549
550         if(!(source && target)) return;
551
552         vlink = ((VObjectData*)source->data)->queue.first;
553
554         if(vlink && (vlink->source==source) && (vlink->target==target)) {
555                 /* remove VerseLink from sending queue */
556                 BLI_remlink(&(((VObjectData*)source->data)->queue), vlink);
557                 /* add VerseLink to dynamic list of VerseLinks */
558                 BLI_dlist_add_item_index(&(((VObjectData*)source->data)->links), vlink, (unsigned int)link_id);
559                 /* send next link from sending queue */
560                 if(((VObjectData*)source->data)->queue.first)
561                         send_verse_link(((VObjectData*)source->data)->queue.first);
562                 /* set up VerseLink variables */
563                 vlink->flag = 0;
564                 vlink->id = link_id;
565                 vlink->target_id = target_id;
566         }
567         else {
568                 /* create new VerseLink */
569                 vlink = create_verse_link(session, source, target, link_id, target_id, label);
570                 /* add VerseLink to dynamic list of VerseLinks */
571                 BLI_dlist_add_item_index(&(((VObjectData*)source->data)->links), vlink, (unsigned int)link_id);
572         }
573
574         target->counter++;
575
576         vlink->post_link_set(vlink);
577 }
578
579 /*
580  * callback function: destroy link between two VerseNodes
581  */
582 static void cb_o_link_destroy(
583                 void *user_data,
584                 VNodeID node_id,
585                 uint16 link_id)
586 {
587         struct VerseSession *session = (VerseSession*)current_verse_session();
588         struct VNode *vnode;
589         struct VLink *vlink;
590
591         if(!session) return;
592
593         vnode = BLI_dlist_find_link(&(session->nodes), (unsigned int)node_id);
594
595         vlink = BLI_dlist_find_link(&(((VObjectData*)vnode->data)->links), link_id);
596
597         if(vlink) {
598                 vlink->target->counter--;
599                 free_verse_link_data(vlink);
600                 BLI_dlist_free_item(&(((VObjectData*)vnode->data)->links), link_id);
601         }
602
603         vlink->post_link_destroy(vlink);
604 }
605
606 void set_object_callbacks(void)
607 {
608         /* position of object was changed */
609         verse_callback_set(verse_send_o_transform_pos_real32, cb_o_transform_pos_real32, NULL);
610         /* rotation of object was changed */
611         verse_callback_set(verse_send_o_transform_rot_real32, cb_o_transform_rot_real32, NULL);
612         /* size of object was changed  */
613         verse_callback_set(verse_send_o_transform_scale_real32, cb_o_transform_scale_real32, NULL);
614         /* new link between nodes was created */
615         verse_callback_set(verse_send_o_link_set, cb_o_link_set, NULL);
616         /* link between nodes was destroyed */
617         verse_callback_set(verse_send_o_link_destroy, cb_o_link_destroy, NULL);
618 }
619
620 #endif