From 30313645b0e533b6cac20ddf5cc1c5a0719d9841 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Sun, 1 Mar 2009 06:36:16 +0000 Subject: [PATCH] Split dissolve_disk into dissolve_vert and dissolve_disk as agreed. also made dissolve vert bmop use the error api, and put in some code to report it to the user in the xkey ui code. Need to make a file in editors/mesh for client code utility functions for bmesh, like e.g. reporting bmesh errors to the user, handling conversion more automatically, etc. --- source/blender/blenkernel/BKE_verse.h | 586 +++++ .../blenkernel/intern/verse_bitmap_node.c | 451 ++++ .../blenkernel/intern/verse_geometry_node.c | 2101 +++++++++++++++++ .../blender/blenkernel/intern/verse_method.c | 523 ++++ source/blender/blenkernel/intern/verse_node.c | 750 ++++++ .../blenkernel/intern/verse_object_node.c | 620 +++++ .../blender/blenkernel/intern/verse_session.c | 480 ++++ source/blender/editors/include/ED_particle.h | 99 + .../editors/space_graph/graph_buttons.c | 213 ++ .../editors/space_view3d/view3d_snap.c | 1148 +++++++++ source/blender/python/BPY_menus.c | 1118 +++++++++ source/blender/python/BPY_menus.h | 128 + source/blender/python/intern/bpy_ui.c | 255 ++ source/blender/python/intern/bpy_ui.h | 31 + 14 files changed, 8503 insertions(+) create mode 100644 source/blender/blenkernel/BKE_verse.h create mode 100644 source/blender/blenkernel/intern/verse_bitmap_node.c create mode 100644 source/blender/blenkernel/intern/verse_geometry_node.c create mode 100644 source/blender/blenkernel/intern/verse_method.c create mode 100644 source/blender/blenkernel/intern/verse_node.c create mode 100644 source/blender/blenkernel/intern/verse_object_node.c create mode 100644 source/blender/blenkernel/intern/verse_session.c create mode 100644 source/blender/editors/include/ED_particle.h create mode 100644 source/blender/editors/space_graph/graph_buttons.c create mode 100644 source/blender/editors/space_view3d/view3d_snap.c create mode 100644 source/blender/python/BPY_menus.c create mode 100644 source/blender/python/BPY_menus.h create mode 100644 source/blender/python/intern/bpy_ui.c create mode 100644 source/blender/python/intern/bpy_ui.h diff --git a/source/blender/blenkernel/BKE_verse.h b/source/blender/blenkernel/BKE_verse.h new file mode 100644 index 00000000000..ee22081b03f --- /dev/null +++ b/source/blender/blenkernel/BKE_verse.h @@ -0,0 +1,586 @@ +/** + * $Id: BKE_verse.h 12931 2007-12-17 18:20:48Z theeth $ + * + * ***** BEGIN GPL/BL DUAL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. The Blender + * Foundation also sells licenses for use in proprietary software under + * the Blender License. See http://www.blender.org/BL/ for information + * about this. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Contributor(s): Jiri Hnidek. + * + * ***** END GPL/BL DUAL LICENSE BLOCK ***** + */ + +/* #define WITH_VERSE */ + +#ifndef BKE_VERSE_H +#define BKE_VERSE_H + +#include "DNA_listBase.h" +#include "BLI_dynamiclist.h" + +#include "verse.h" +#include "verse_ms.h" + +struct VNode; +struct VerseEdge; + +/* + * Verse Edge Hash (similar to edit edge hash) + */ +#define VEDHASHSIZE (512*512) +#define VEDHASH(a, b) ((a1 is 3D bitmap */ + /* blender internals */ + uint16 t_width; /* = (width/VN_B_TILE_SIZE + 1)*VN_B_TILE_SIZE */ + uint16 t_height; /* = (height/VN_B_TILE_SIZE + 1)*VN_B_TILE_SIZE */ + void *image; /* pointer at image */ + /* client dependent methods */ + void (*post_bitmap_dimension_set)(struct VNode *vnode); + void (*post_bitmap_layer_create)(struct VBitmapLayer *vblayer); + void (*post_bitmap_layer_destroy)(struct VBitmapLayer *vblayer); + void (*post_bitmap_tile_set)(struct VBitmapLayer *vblayer, unsigned int xs, unsigned int ys); +}VBitmapData; + +/* + * data of geometry node + */ +typedef struct VGeomData { + struct DynamicList layers; /* dynamic list with access array of Layers */ + struct VLink *vlink; /* pointer at VerseLink connecting object node and geom node */ + struct ListBase queue; /* queue of our layers waiting for receiving from verse server */ + void *mesh; /* pointer at Mesh (object node) */ + void *editmesh; /* pointer at EditMesh (edit mode) */ + struct HashVerseEdge *hash; /* verse edge hash */ + struct ListBase edges; /* list of fake verse edges */ + /* client dependent methods */ + void (*post_vertex_create)(struct VerseVert *vvert); + void (*post_vertex_set_xyz)(struct VerseVert *vvert); + void (*post_vertex_delete)(struct VerseVert *vvert); + void (*post_vertex_free_constraint)(struct VerseVert *vvert); + void (*post_polygon_create)(struct VerseFace *vface); + void (*post_polygon_set_corner)(struct VerseFace *vface); + void (*post_polygon_delete)(struct VerseFace *vface); + void (*post_polygon_free_constraint)(struct VerseFace *vface); + void (*post_geometry_free_constraint)(struct VNode *vnode); + void (*post_polygon_set_uint8)(struct VerseFace *vface); +} VGeomData; + +/* + * data of object node + */ +typedef struct VObjectData { + struct DynamicList links; /* dynamic list with access array of links between other nodes */ + struct ListBase queue; /* queue of links waiting for sending and receiving from verse server */ + float pos[3]; /* position of object VerseNode */ + float quat[4]; /* rotation of object VerseNode stored in quat */ + float scale[3]; /* scale of object VerseNode */ + void *object; /* pointer at object */ + short flag; /* flag: POS_RECEIVE_READY, ROT_RECEIVE_READY. SCALE_RECEIVE_READY */ + /* client dependent methods */ +/* void (*post_transform)(struct VNode *vnode);*/ + void (*post_transform_pos)(struct VNode *vnode); + void (*post_transform_rot)(struct VNode *vnode); + void (*post_transform_scale)(struct VNode *vnode); + void (*post_object_free_constraint)(struct VNode *vnode); +} VObjectData; + +/* + * Verse Tag + */ +typedef struct VTag { + struct VTag *next, *prev; + /* verse data*/ + struct VTagGroup *vtaggroup; /* pointer at Verse Tag Group */ + uint16 id; /* id of this tag */ + char *name; /* name of this tag*/ + VNTagType type; /* type: VN_TAG_BOOLEAN, VN_TAG_UINT32, VN_TAG_REAL64, VN_TAG_REAL64_VEC3, + VN_TAG_LINK, VN_TAG_ANIMATION, VN_TAG_BLOB */ + VNTag *tag; /* pointer at value (enum: vboolean, vuint32, vreal64, vstring, + vreal64_vec3, vlink, vanimation, vblob)*/ + /* blender internals */ + void *value; /* pointer at blender value */ +} VTag; + +/* + * Verse Tag Group (verse tags are grouped in tag groups) + */ +typedef struct VTagGroup { + struct VTagGroup *next, *prev; + /* verse data*/ + struct VNode *vnode; /* pointer at Verse Node */ + uint16 id; /* id of this tag group */ + char *name; /* name of this tag group */ + /* blender internals */ + struct DynamicList tags; /* dynamic list with access array containing tags */ + struct ListBase queue; /* list of tags waiting for receiving from verse server */ + /* client dependent methods */ + void (*post_tag_change)(struct VTag *vatg); + void (*post_taggroup_create)(struct VTagGroup *vtaggroup); +} VTagGroup; + + /* + * Verse Method Group + */ +typedef struct VMethodGroup +{ + struct VMethodGroup *next, *prev; + uint16 group_id; + char name[16]; + struct ListBase methods; +} VMethodGroup; + +/* + * Verse Method + */ +typedef struct VMethod +{ + struct VMethod *next, *prev; + uint16 id; + char name[500]; + uint8 param_count; + VNOParamType *param_type; + char **param_name; +} VMethod; + +/* + * Verse Node + */ +typedef struct VNode { + struct VNode *next, *prev; + /* verse data*/ + struct VerseSession *session; /* session pointer */ + VNodeID id; /* node id */ + VNodeID owner_id; /* owner's id of this node */ + char *name; /* name of this node */ + uint32 type; /* type of node (V_NT_OBJECT, V_NT_GEOMETRY, V_NT_BITMAP) */ + /* blender internals */ + char flag; /* flags: NODE_SENT, NODE_RECEIVED, NODE_DELTED, NODE_OBSOLETE */ + struct DynamicList taggroups; /* dynamic list with access array of taggroups */ + struct ListBase methodgroups; /* method groups */ + struct ListBase queue; /* list of taggroups waiting for receiving from verse server */ + void *data; /* generic pointer at some data (VObjectData, VGeomData, ...) */ + int counter; /* counter of verse link pointing at this vnode (vlink->target) */ + /* client dependent methods */ + void (*post_node_create)(struct VNode *vnode); + void (*post_node_destroy)(struct VNode *vnode); + void (*post_node_name_set)(struct VNode *vnode); +#ifdef VERSECHAT + /* verse chat */ + int chat_flag; /* CHAT_LOGGED, CHAT_NOTLOGGED */ +#endif +} VNode; + + +/* + * Verse Session: verse client can be connected to several verse servers + * it is neccessary to store some information about each session + */ +typedef struct VerseSession { + struct VerseSession *next, *prev; + /* verse data */ + VSession *vsession; /* pointer at VSeesion (verse.h) */ + uint32 avatar; /* id of avatar */ + char *address; /* string containg IP/domain name of verse server and number of port */ + void *connection; /* no clue */ + uint8 *host_id; /* no clue */ + /* blender internals */ + short flag; /* flag: VERSE_CONNECTING, VERSE_CONNECTED */ + DynamicList nodes; /* list of verse nodes */ + ListBase queue; /* list of nodes waiting for sending to verse server */ + unsigned int counter; /* count of events, when connection wasn't accepted */ + /* client dependent methods */ + void (*post_connect_accept)(struct VerseSession *session); + void (*post_connect_terminated)(struct VerseSession *session); + void (*post_connect_update)(struct VerseSession *session); +} VerseSession; + +typedef struct VerseServer { + struct VerseServer *next, *prev; + char *name; /* human-readable server name */ + char *ip; /* string containing IP/domain name of verse server and number of port */ + short flag; /* flag: VERSE_CONNECTING, VERSE_CONNECTED */ + struct VerseSession *session; /* pointer to related session */ +} VerseServer; +/* + * list of post callback functions + */ +typedef struct PostCallbackFunction { + void (*function)(void *arg); + void *param; +} PostCallbackFunction; + +/* VerseSession->flag */ +#define VERSE_CONNECTING 1 +#define VERSE_CONNECTED 2 +#define VERSE_AUTOSUBSCRIBE 4 + +/* max VerseSession->counter value */ +#define MAX_UNCONNECTED_EVENTS 100 + +/* VNode flags */ +#define NODE_SENT 1 +#define NODE_RECEIVED 2 +#define NODE_DELTED 4 +#define NODE_OBSOLETE 8 + +#ifdef VERSECHAT +#define CHAT_NOTLOGGED 0 +#define CHAT_LOGGED 1 +#endif + +/* VLayer flags */ +#define LAYER_SENT 1 +#define LAYER_RECEIVED 2 +#define LAYER_DELETED 4 +#define LAYER_OBSOLETE 8 + +/* VLink->flag */ +#define LINK_SEND_READY 1 + +/* VObjectData->flag */ +#define POS_RECEIVE_READY 1 +#define ROT_RECEIVE_READY 2 +#define SCALE_RECEIVE_READY 4 +#define POS_SEND_READY 8 +#define ROT_SEND_READY 16 +#define SCALE_SEND_READY 32 + +/* VLayer->content */ +#define VERTEX_LAYER 0 +#define POLYGON_LAYER 1 + +/* VerseVert->flag */ +#define VERT_DELETED 1 /* vertex delete command was received from verse server */ +#define VERT_RECEIVED 2 /* VerseVert was received from verse server (is not in sending queue) */ +#define VERT_LOCKED 4 /* VerseVert is ready to send local position to verse server */ +#define VERT_POS_OBSOLETE 8 /* position of vertex was changed during sending to verse server */ +#define VERT_OBSOLETE 16 /* vertex delete command was sent to verse server; it means, that + * no information related to this vertex shoudln't be sent to verse + * until verse vertex is completely deleted ... then this vertex id + * can be reused again for new vertex */ + +/* VerseFace->flag */ +#define FACE_SEND_READY 1 /* VerseFace is ready for sending to verse server */ +#define FACE_RECEIVED 2 /* VerseFace was received from verse server */ +#define FACE_SENT 4 /* VerseFace was sent to verse server and we expect receiving from verse server */ +#define FACE_DELETED 8 /* delete command was sent to verse server */ +#define FACE_CHANGED 16 /* VerseFace was only changed not created */ +#define FACE_OBSOLETE 32 /* VerseFace was changed during sending to verse server */ + +/* Queue type */ +#define VERSE_NODE 1 +#define VERSE_LINK 2 +#define VERSE_LAYER 3 +#define VERSE_VERT 4 +#define VERSE_FACE 5 + +#define VERSE_TAG 6 +#define VERSE_TAG_GROUP 7 + +#define VERSE_VERT_UINT32 8 +#define VERSE_VERT_REAL32 9 +#define VERSE_VERT_VEC_REAL32 10 + +#define VERSE_FACE_UINT8 11 +#define VERSE_FACE_UINT32 12 +#define VERSE_FACE_REAL32 13 +#define VERSE_FACE_QUAT_UINT32 14 +#define VERSE_FACE_QUAT_REAL32 15 + +/* Verse Bitmap Layer flags */ +#define VBLAYER_SUBSCRIBED 1 + +/* function prototypes */ + +/* functions from verse_session.c */ +void set_verse_session_callbacks(void); +struct VerseSession *versesession_from_vsession(VSession *vsession); +struct VerseSession *current_verse_session(void); +struct VerseSession *create_verse_session(const char *name, const char *pass, const char *address, uint8 *expected_key); +void free_verse_session(struct VerseSession *session); +void b_verse_update(void); +void b_verse_ms_get(void); +void b_verse_connect(char *address); +void end_verse_session(struct VerseSession *session); +void end_all_verse_sessions(void); + +/* functions from verse_node.c */ +void send_verse_tag(struct VTag *vtag); +void send_verse_taggroup(struct VTagGroup *vtaggroup); +void send_verse_node(struct VNode *vnode); +void free_verse_node_data(struct VNode *vnode); +void free_verse_node(struct VNode *vnode); +struct VNode* lookup_vnode(VerseSession *session, VNodeID node_id); +struct VNode* create_verse_node(VerseSession *session, VNodeID node_id, uint8 type, VNodeID owner_id); +void set_node_callbacks(void); + +/* functions from verse_object_node.c */ +struct VLink *find_unsent_parent_vlink(struct VerseSession *session, struct VNode *vnode); +struct VLink *find_unsent_child_vlink(struct VerseSession *session, struct VNode *vnode); +struct VLink *create_verse_link(VerseSession *session, struct VNode *source, struct VNode *target, uint16 link_id, uint32 target_id, const char *label); +void send_verse_object_position(struct VNode *vnode); +void send_verse_object_rotation(struct VNode *vnode); +void send_verse_object_scale(struct VNode *vnode); +void send_verse_link(struct VLink *vlink); + +void free_object_data(struct VNode *vnode); +void set_object_callbacks(void); +struct VObjectData *create_object_data(void); + + +/* functions from verse_method.c */ +void free_verse_methodgroup(VMethodGroup *vmg); +#ifdef VERSECHAT +void send_say(const char *chan, const char *utter); +void send_login(struct VNode *vnode); +void send_logout(struct VNode *vnode); +void send_join(struct VNode *vnode, const char *chan); +void send_leave(struct VNode *vnode, const char *chan); +#endif +void set_method_callbacks(void); + +/* functions from verse_geometry_node.c */ +struct VerseFace* create_verse_face(struct VLayer *vlayer, uint32 polygon_id, uint32 v0, uint32 v1, uint32 v2, uint32 v3); +struct VerseVert* create_verse_vertex(struct VLayer *vlayer, uint32 vertex_id, real32 x, real32 y, real32 z); +struct VLayer *create_verse_layer(struct VNode *vnode, VLayerID layer_id, const char *name, VNGLayerType type, uint32 def_integer, real64 def_real); +struct VGeomData *create_geometry_data(void); + +void send_verse_layer(struct VLayer *vlayer); + +void send_verse_face_corner_quat_real32(struct quat_real32_item *item, short type); +void send_verse_face_corner_quat_uint32(struct quat_uint32_item *item, short type); +void send_verse_face_real32(struct real32_item *item, short type); +void send_verse_face_uint32(struct uint32_item *item, short type); +void send_verse_face_uint8(struct uint8_item *item, short type); + +void send_verse_vert_vec_real32(struct vec_real32_item *item, short type); +void send_verse_vert_real32(struct real32_item *item, short type); +void send_verse_vert_uint32(struct uint32_item *item, short type); + +void send_verse_vertex_delete(struct VerseVert *vvert); +void send_verse_vertex(struct VerseVert *vvert); +void send_verse_face_delete(struct VerseFace *vface); + +void destroy_geometry(struct VNode *vnode); + +struct VLayer* find_verse_layer_type(struct VGeomData *geom, short content); +void add_item_to_send_queue(struct ListBase *lb, void *item, short type); +void free_geom_data(struct VNode *vnode); +void set_geometry_callbacks(void); + +/* functions prototypes from verse_bitmap.c */ +void set_bitmap_callbacks(void); +void free_bitmap_layer_data(struct VBitmapLayer *vblayer); +struct VBitmapLayer *create_bitmap_layer(struct VNode *vnode, VLayerID layer_id, const char *name, VNBLayerType type); +void free_bitmap_node_data(struct VNode *vnode); +struct VBitmapData *create_bitmap_data(void); + +#endif diff --git a/source/blender/blenkernel/intern/verse_bitmap_node.c b/source/blender/blenkernel/intern/verse_bitmap_node.c new file mode 100644 index 00000000000..d27f7a13f02 --- /dev/null +++ b/source/blender/blenkernel/intern/verse_bitmap_node.c @@ -0,0 +1,451 @@ +/** + * $Id: verse_bitmap_node.c 12931 2007-12-17 18:20:48Z theeth $ + * + * ***** BEGIN GPL/BL DUAL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. The Blender + * Foundation also sells licenses for use in proprietary software under + * the Blender License. See http://www.blender.org/BL/ for information + * about this. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Contributor(s): Jiri Hnidek. + * + * ***** END GPL/BL DUAL LICENSE BLOCK ***** + */ + +#ifdef WITH_VERSE + +#include + +#include "MEM_guardedalloc.h" + +#include "DNA_listBase.h" + +#include "BLI_dynamiclist.h" +#include "BLI_blenlib.h" + +#include "BIF_verse.h" + +#include "BKE_verse.h" + +#include "verse.h" + +/* function prototypes of static functions */ +static void cb_b_dimension_set(void *user_data, VNodeID node_id, uint16 width, uint16 height, uint16 depth); +static void cb_b_layer_create(void *user_data, VNodeID node_id, VLayerID layer_id, const char *name, VNBLayerType type); +static void cb_b_layer_destroy(void *user_data, VNodeID node_id, VLayerID layer_id); +static void cb_b_tile_set(void *user_data, VNodeID node_id, VLayerID layer_id, uint16 tile_x, uint16 tile_y, uint16 z, VNBLayerType type, const VNBTile *tile); + +static void change_layer_dimension( + VBitmapLayer *vblayer, + unsigned int old_width, + unsigned int old_height, + unsigned int t_old_width, + unsigned int t_old_height); +static void *alloc_verse_bitmap_layer_data(struct VBitmapLayer *vblayer); + +/* + * resize/crop verse bitmap layer + */ +static void change_layer_dimension( + VBitmapLayer *vblayer, + unsigned int old_width, + unsigned int old_height, + unsigned int t_old_width, + unsigned int t_old_height) +{ + struct VNode *vnode = vblayer->vnode; + unsigned int t_width = ((VBitmapData*)(vnode->data))->t_width; + unsigned int width = ((VBitmapData*)(vnode->data))->width; + unsigned int height = ((VBitmapData*)(vnode->data))->height; + unsigned int x, y, i, j; + + i = j = 0; + + /* "copy" old data to new data */ + if(vblayer->type==VN_B_LAYER_UINT8) { + unsigned char *data = (unsigned char*)vblayer->data; + /* allocate new verse bitmap layer data */ + unsigned char *new_data = (unsigned char*)alloc_verse_bitmap_layer_data(vblayer); + for(y=0; ydata); + vblayer->data = new_data; + } +} + +/* + * free data stored in verse bitmap layer + */ +void free_bitmap_layer_data(VBitmapLayer *vblayer) +{ + struct VerseSession *session = vblayer->vnode->session; + + /* free name of bitmap layer */ + MEM_freeN(vblayer->name); + + /* unsubscribe from verse bitmap layer */ + if(session->flag & VERSE_CONNECTED) + verse_send_b_layer_unsubscribe(vblayer->vnode->id, vblayer->id); + + /* free image data of bitmap layer */ + if(vblayer->data) MEM_freeN(vblayer->data); +} + +/* + * allocate data of verse bitmap layer + */ +static void *alloc_verse_bitmap_layer_data(VBitmapLayer *vblayer) +{ + struct VNode *vnode = vblayer->vnode; + unsigned int t_width = ((VBitmapData*)(vnode->data))->t_width; + unsigned int t_height = ((VBitmapData*)(vnode->data))->t_height; + unsigned int size; + void *data; + + size = t_width*t_height; + + /* allocation of own data stored in verse bitmap layer */ + switch (vblayer->type) { + case VN_B_LAYER_UINT1: + data = (void*)MEM_mallocN(sizeof(unsigned char)*size, "VBLayer data uint1"); + break; + case VN_B_LAYER_UINT8: + data = (void*)MEM_mallocN(sizeof(unsigned char)*size, "VBLayer data uint8"); + break; + case VN_B_LAYER_UINT16: + data = (void*)MEM_mallocN(sizeof(unsigned int)*size, "VBLayer data uint16"); + break; + case VN_B_LAYER_REAL32: + data = (void*)MEM_mallocN(sizeof(float)*size, "VBLayer data float16"); + break; + case VN_B_LAYER_REAL64: + data = (void*)MEM_mallocN(sizeof(double)*size, "VBLayer data float32"); + break; + default: + data = NULL; + break; + } + + return data; +} + +/* + * create verse bitmap layer + */ +VBitmapLayer *create_bitmap_layer( + VNode *vnode, + VLayerID layer_id, + const char *name, + VNBLayerType type) +{ + struct VBitmapLayer *vblayer; + unsigned int width = ((VBitmapData*)(vnode->data))->width; + unsigned int height = ((VBitmapData*)(vnode->data))->height; + + /* allocate memory for own verse bitmap layer */ + vblayer = (VBitmapLayer*)MEM_mallocN(sizeof(VBitmapLayer), "Verse Bitmap Layer"); + + /* verse bitmap layer will include pointer at parent verse node and own id */ + vblayer->vnode = vnode; + vblayer->id = layer_id; + + /* name of verse layer */ + vblayer->name = (char*)MEM_mallocN(sizeof(char)*(strlen(name)+1), "Verse Bitmap Layer name"); + vblayer->name[0] = '\0'; + strcpy(vblayer->name, name); + + /* type of data stored in verse bitmap layer */ + vblayer->type = type; + + /* we can allocate memory for layer data, when we know dimmension of layers; when + * we don't know it, then we will allocate this data when we will receive dimmension */ + if(width==0 || height==0) + vblayer->data = NULL; + else + vblayer->data = alloc_verse_bitmap_layer_data(vblayer); + + vblayer->flag = 0; + + return vblayer; +} + +/* + * free data of bitmap node + */ +void free_bitmap_node_data(VNode *vnode) +{ + if(vnode->data) { + struct VBitmapLayer *vblayer = (VBitmapLayer*)((VBitmapData*)(vnode->data))->layers.lb.first; + + /* free all VerseLayer data */ + while(vblayer) { + free_bitmap_layer_data(vblayer); + vblayer = vblayer->next; + } + + /* free all VerseLayers */ + BLI_dlist_destroy(&(((VGeomData*)vnode->data)->layers)); + } +} + +/* + * create data of bitmap node + */ +VBitmapData *create_bitmap_data() +{ + struct VBitmapData *vbitmap; + + vbitmap = (VBitmapData*)MEM_mallocN(sizeof(VBitmapData), "Verse Bitmap Data"); + + BLI_dlist_init(&(vbitmap->layers)); + vbitmap->queue.first = vbitmap->queue.last = NULL; + + vbitmap->width = 0; + vbitmap->height = 0; + vbitmap->depth = 0; + + vbitmap->image = NULL; + + vbitmap->post_bitmap_dimension_set = post_bitmap_dimension_set; + vbitmap->post_bitmap_layer_create = post_bitmap_layer_create; + vbitmap->post_bitmap_layer_destroy = post_bitmap_layer_destroy; + vbitmap->post_bitmap_tile_set = post_bitmap_tile_set; + + return vbitmap; +} + +/* + * callback function, dimension of image was changed, it is neccessary to + * crop all layers + */ +static void cb_b_dimension_set( + void *user_data, + VNodeID node_id, + uint16 width, + uint16 height, + uint16 depth) +{ + struct VerseSession *session = (VerseSession*)current_verse_session(); + struct VNode *vnode; + struct VBitmapLayer *vblayer; + unsigned int old_width, old_height, t_old_width, t_old_height; + + if(!session) return; + + vnode = (VNode*)BLI_dlist_find_link(&(session->nodes), (unsigned int)node_id); + if(!vnode) return; + +#ifdef VERSE_DEBUG_PRINT + printf("\t cb_b_dimension_set()\n"); +#endif + + /* backup old width and height */ + old_width = ((VBitmapData*)(vnode->data))->width; + old_height = ((VBitmapData*)(vnode->data))->height; + t_old_width = ((VBitmapData*)(vnode->data))->t_width; + t_old_height = ((VBitmapData*)(vnode->data))->t_height; + + /* set up new dimension of layers */ + ((VBitmapData*)(vnode->data))->width = width; + ((VBitmapData*)(vnode->data))->height = height; + ((VBitmapData*)(vnode->data))->depth = depth; + + /* we cache t_width because tiles aren't one pixel width */ + if((width % VN_B_TILE_SIZE)!=0) + ((VBitmapData*)(vnode->data))->t_width = (width/VN_B_TILE_SIZE + 1)*VN_B_TILE_SIZE; + else + ((VBitmapData*)(vnode->data))->t_width = width; + + /* we cache t_height because tiles aren't one pixel height */ + if((height % VN_B_TILE_SIZE)!=0) + ((VBitmapData*)(vnode->data))->t_height = (height/VN_B_TILE_SIZE + 1)*VN_B_TILE_SIZE; + else + ((VBitmapData*)(vnode->data))->t_height = height; + + /* crop resize all layers */ + vblayer = ((VBitmapData*)vnode->data)->layers.lb.first; + + while(vblayer) { + /* when this callback function received after cb_b_layer_create, + * then we have to allocate memory for verse bitmap layer data */ + if(!vblayer->data) vblayer->data = alloc_verse_bitmap_layer_data(vblayer); + /* crop/resize all verse bitmap layers */ + else change_layer_dimension(vblayer, old_width, old_height, t_old_width, t_old_height); + + vblayer = vblayer->next; + } + + /* post callback function */ + ((VBitmapData*)(vnode->data))->post_bitmap_dimension_set(vnode); +} + +/* + * callback function, new layer channel of image was created + */ +static void cb_b_layer_create( + void *user_data, + VNodeID node_id, + VLayerID layer_id, + const char *name, + VNBLayerType type) +{ + struct VerseSession *session = (VerseSession*)current_verse_session(); + struct VNode *vnode; + struct VBitmapLayer *vblayer; + + if(!session) return; + + vnode = (VNode*)BLI_dlist_find_link(&(session->nodes), (unsigned int)node_id); + if(!vnode) return; + +#ifdef VERSE_DEBUG_PRINT + printf("\t cb_b_layer_create()\n"); +#endif + + /* when no layer exists, then new layer will be created */ + vblayer = create_bitmap_layer(vnode, layer_id, name, type); + + /* add verse bitmap layer to list of layers */ + BLI_dlist_add_item_index(&((VBitmapData*)vnode->data)->layers, vblayer, layer_id); + + /* post callback function */ + ((VBitmapData*)(vnode->data))->post_bitmap_layer_create(vblayer); + +} + +/* + * callback function, existing layer of image was destroyed + */ +static void cb_b_layer_destroy( + void *user_data, + VNodeID node_id, + VLayerID layer_id) +{ + struct VerseSession *session = (VerseSession*)current_verse_session(); + struct VNode *vnode; + struct VBitmapLayer *vblayer; + + if(!session) return; + + /* find node of this layer*/ + vnode = (VNode*)BLI_dlist_find_link(&(session->nodes), (unsigned int)node_id); + if(!vnode) return; + + vblayer = (VBitmapLayer*)BLI_dlist_find_link(&(((VBitmapData*)vnode->data)->layers), layer_id); + if(!vblayer) return; + +#ifdef VERSE_DEBUG_PRINT + printf("\t cb_b_layer_destroy()\n"); +#endif + + /* remove verse bitmap layer from list of layers */ + BLI_dlist_rem_item(&(((VBitmapData*)vnode->data)->layers), layer_id); + + /* post callback function */ + ((VBitmapData*)(vnode->data))->post_bitmap_layer_destroy(vblayer); + + /* free data of verse bitmap layer */ + free_bitmap_layer_data(vblayer); + + /* free verse bitmap layer */ + MEM_freeN(vblayer); +} + +/* + * callback function, small part (8x8 pixels) was changed + */ +static void cb_b_tile_set( + void *user_data, + VNodeID node_id, + VLayerID layer_id, + uint16 tile_x, + uint16 tile_y, + uint16 z, + VNBLayerType type, + const VNBTile *tile) +{ + struct VerseSession *session = (VerseSession*)current_verse_session(); + struct VNode *vnode; + struct VBitmapLayer *vblayer; + unsigned int x, y, xs, ys, width, height, t_height, t_width, i, j; + + if(!session) return; + + /* try to find verse node in dynamic list nodes */ + vnode = (VNode*)BLI_dlist_find_link(&(session->nodes), (unsigned int)node_id); + if(!vnode) return; + + /* try to find verse bitmap layer in list of layers */ + vblayer = (VBitmapLayer*)BLI_dlist_find_link(&(((VBitmapData*)vnode->data)->layers), layer_id); + if(!vblayer) return; + + /* we have to have allocated memory for bitmap layer */ + if(!vblayer->data) return; + + width = ((VBitmapData*)vnode->data)->width; + height = ((VBitmapData*)vnode->data)->height; + + /* width of verse image including all tiles */ + t_height = ((VBitmapData*)vnode->data)->t_height; + /* height of verse image including all tiles */ + t_width = ((VBitmapData*)vnode->data)->t_width; + +#ifdef VERSE_DEBUG_PRINT + printf("\t cb_b_tile_set()\n"); +#endif + + xs = tile_x*VN_B_TILE_SIZE; + ys = tile_y*VN_B_TILE_SIZE; + + /* initial position in one dimension vblayer->data (y_start*width + x_start) */ + i = ys*t_width + xs; + /* intial position in one dimension tile array */ + j = 0; + + if(type==VN_B_LAYER_UINT8) { + unsigned char *data = (unsigned char*)vblayer->data; + for(y=ys; yvuint8[j]; + } + + /* post callback function */ + ((VBitmapData*)(vnode->data))->post_bitmap_tile_set(vblayer, xs, ys); +} + +/* + * set up all callbacks functions for image nodes + */ +void set_bitmap_callbacks(void) +{ + /* dimension (size) of bitmap was set up or changes (image will be croped) */ + verse_callback_set(verse_send_b_dimensions_set, cb_b_dimension_set, NULL); + + /* new layer (chanell) of image was added or created */ + verse_callback_set(verse_send_b_layer_create, cb_b_layer_create, NULL); + + /* existing layer was destroyed */ + verse_callback_set(verse_send_b_layer_destroy, cb_b_layer_destroy, NULL); + + /* some tile (small part 8x8 pixels of image was changed) */ + verse_callback_set(verse_send_b_tile_set, cb_b_tile_set, NULL); +} + +#endif + diff --git a/source/blender/blenkernel/intern/verse_geometry_node.c b/source/blender/blenkernel/intern/verse_geometry_node.c new file mode 100644 index 00000000000..a53ad2cb627 --- /dev/null +++ b/source/blender/blenkernel/intern/verse_geometry_node.c @@ -0,0 +1,2101 @@ +/** + * $Id: verse_geometry_node.c 12931 2007-12-17 18:20:48Z theeth $ + * + * ***** BEGIN GPL/BL DUAL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. The Blender + * Foundation also sells licenses for use in proprietary software under + * the Blender License. See http://www.blender.org/BL/ for information + * about this. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Contributor(s): Jiri Hnidek. + * + * ***** END GPL/BL DUAL LICENSE BLOCK ***** + */ + +#ifdef WITH_VERSE + +#include + +#include "MEM_guardedalloc.h" + +#include "DNA_listBase.h" + +#include "BLI_dynamiclist.h" +#include "BLI_blenlib.h" +#include "BLI_arithb.h" + +#include "BKE_verse.h" +#include "BKE_utildefines.h" + +#include "BIF_verse.h" + +#include "verse.h" + +/* function prototypes of static functions */ + +/* test functions for callback functions */ +static char test_polygon_set_corner_uint32(uint32 v0, uint32 v1, uint32 v2, uint32 v3); + +/* callback functions */ +static void cb_g_layer_create(void *user_data, VNodeID node_id, VLayerID layer_id, const char *name, VNGLayerType type, uint32 def_integer, real64 def_real); +static void cb_g_layer_destroy(void *user_data, VNodeID node_id, VLayerID layer_id); +static void cb_g_vertex_set_xyz_real32(void *user_data, VNodeID node_id, VLayerID layer_id, uint32 vertex_id, real32 x, real32 y, real32 z); +static void cb_g_polygon_set_corner_uint32(void *user_data, VNodeID node_id, VLayerID layer_id, uint32 polygon_id, uint32 v0, uint32 v1, uint32 v2, uint32 v3); +static void cb_g_vertex_delete_real32(void *user_data, VNodeID node_id, uint32 vertex_id); +static void cb_g_polygon_delete(void *user_data, VNodeID node_id, uint32 polygon_id); +static void cb_g_crease_set_edge(void *user_data, VNodeID node_id, const char *layer, uint32 def_crease); +static void cb_g_crease_set_vertex(void *user_data, VNodeID node_id, const char *layer, uint32 def_crease); + +/* other static functions */ + +static void free_unneeded_verseverts_of_verseface(struct VNode *vnode, struct VerseFace *vface); +static void free_verse_vertex(struct VLayer *vlayer, struct VerseVert *vvert); +static void free_verse_face(struct VLayer *vlayer, struct VerseFace *vface); +static void free_verse_layer_data(struct VNode *vnode, struct VLayer *vlayer); + +static void send_verse_face(struct VerseFace *vface); + +static VerseVert* find_verse_vert_in_queue(struct VLayer *vlayer, VNodeID node_id, uint32 vertex_id, real32 x, real32 y, real32 z); +static VerseFace* find_verse_face_in_queue(struct VLayer *vlayer, VNodeID node_id, uint32 polygon_id, uint32 v0, uint32 v1, uint32 v2, uint32 v3); + +static unsigned short test_incoming_verseface(struct VGeomData *geom, struct VerseFace *vface); +static void find_unsent_faces(struct VNode *vnode, struct VerseVert *vvert); +static void find_vlayer_orphans(struct VNode *vnode, struct VerseVert *vvert); +static void move_face_orphan_to_dlist(struct VNode *vnode, struct VLayer *vlayer, struct VerseFace *vface); +static void increase_verse_verts_references(struct VerseFace *vface); +static void recalculate_verseface_normals(struct VNode *vnode); + +/* verse edge functions */ +static VerseEdge* find_verse_edge(struct VNode *vnode, uint32 v0, uint32 v1); +static void insert_verse_edgehash(struct VNode *vnode, struct VerseEdge *vedge); +static void remove_verse_edgehash(struct VNode *vnode, struct VerseEdge *vedge); +static void remove_verse_edge(struct VNode *vnode, uint32 v0, uint32 v1); +static void add_verse_edge(struct VNode *vnode, uint32 v0, uint32 v1); +static void update_edgehash_of_deleted_verseface(struct VNode *vnode, struct VerseFace *vface); +static void update_edgehash_of_changed_verseface(struct VNode *vnode, struct VerseFace *vface, uint32 v0, uint32 v1, uint32 v2, uint32 v3); +static void update_edgehash_of_new_verseface(struct VNode *vnode, uint32 v0, uint32 v1, uint32 v2, uint32 v3); + +/* + * recalcute normals of all VerseFaces + */ +static void recalculate_verseface_normals(VNode *vnode) +{ + struct VLayer *vert_layer, *face_layer; + struct VerseFace *vface; + struct VerseVert *vvert; + + if(vnode->type != V_NT_GEOMETRY) return; + + vert_layer = find_verse_layer_type((VGeomData*)vnode->data, VERTEX_LAYER); + face_layer = find_verse_layer_type((VGeomData*)vnode->data, POLYGON_LAYER); + + vvert = vert_layer->dl.lb.first; + while(vvert) { + vvert->no[0] = vvert->no[1] = vvert->no[2] = 0.0; + vvert = vvert->next; + } + + vface = face_layer->dl.lb.first; + while(vface) { + /* calculate face normals */ + if(vface->vvert3) { + CalcNormFloat4(vface->vvert0->co, vface->vvert1->co, + vface->vvert2->co, vface->vvert3->co, vface->no); + VecAddf(vface->vvert3->no, vface->vvert3->no, vface->no); + } + else + CalcNormFloat(vface->vvert0->co, vface->vvert1->co, + vface->vvert2->co, vface->no); + + /* calculate vertex normals ... it is averadge of all face normals using the vertex */ + VecAddf(vface->vvert0->no, vface->vvert0->no, vface->no); + VecAddf(vface->vvert1->no, vface->vvert1->no, vface->no); + VecAddf(vface->vvert2->no, vface->vvert2->no, vface->no); + + vface = vface->next; + } + + /* we have to normalize all vertex normals */ + vvert = vert_layer->dl.lb.first; + while(vvert) { + Normalize(vvert->no); + vvert = vvert->next; + } +} + +/* + * add created item to the queue and send it if possible + */ +void add_item_to_send_queue(ListBase *lb, void *item, short type) +{ + struct VNode *vnode; + struct VLayer *vlayer; + struct VerseVert *vvert; + struct VerseFace *vface; + + /* this prevent from adding duplicated faces */ + if(type==VERSE_FACE) { + struct Link *link = (Link*)lb->first; + while(link) { + if(link==item) { + if(((VerseFace*)item)->flag & FACE_SENT) { +/* printf("\tverse face %d marked as OBSOLETE\n", ((VerseFace*)item)->id);*/ + ((VerseFace*)item)->flag |= FACE_OBSOLETE; + } + return; + } + link = link->next; + } + } + + /* add item to sending queue (two way dynamic list) */ + BLI_addtail(lb, item); + + /* send item, when it is possible */ + switch (type) { + case VERSE_NODE: /* only first node in queue can be sent */ + if(lb->first==lb->last) + send_verse_node((VNode*)item); + break; + case VERSE_LINK: /* both object between have to exist */ + if(((VLink*)item)->flag & LINK_SEND_READY) + send_verse_link((VLink*)item); + break; + case VERSE_LAYER: + if(((VLayer*)item)->vnode->flag & NODE_RECEIVED) + send_verse_layer((VLayer*)item); + break; + case VERSE_VERT: + if(((VerseVert*)item)->vlayer->flag & LAYER_RECEIVED) + send_verse_vertex((VerseVert*)item); + break; + case VERSE_FACE: /* all vertexes of face have to be received */ + if(((VerseFace*)item)->flag & FACE_SEND_READY) + send_verse_face((VerseFace*)item); + break; + case VERSE_TAG: + send_verse_tag((VTag*)item); + break; + case VERSE_TAG_GROUP: + send_verse_taggroup((VTagGroup*)item); + break; + case VERSE_VERT_UINT32: /* parent item has to exist */ + vnode = (((uint32_item*)item)->vlayer)->vnode; + vlayer = (VLayer*)BLI_dlist_find_link(&(((VGeomData*)vnode->data)->layers), 0 ); + vvert = (VerseVert*)BLI_dlist_find_link(&(vlayer->dl), ((uint32_item*)item)->id ); + if(vvert != NULL) + send_verse_vert_uint32((uint32_item*)item, type); + break; + case VERSE_VERT_REAL32: /* parent item has to exist */ + vnode = (((real32_item*)item)->vlayer)->vnode; + vlayer = (VLayer*)BLI_dlist_find_link(&(((VGeomData*)vnode->data)->layers), 0 ); + vvert = (VerseVert*)BLI_dlist_find_link(&(vlayer->dl), ((real32_item*)item)->id ); + if( vvert != NULL) + send_verse_vert_real32((real32_item*)item, type); + break; + case VERSE_VERT_VEC_REAL32: /* parent item has to exist */ + vnode = (((vec_real32_item*)item)->vlayer)->vnode; + vlayer = (VLayer*)BLI_dlist_find_link(&(((VGeomData*)vnode->data)->layers), 0 ); + vvert = (VerseVert*)BLI_dlist_find_link(&(vlayer->dl), ((vec_real32_item*)item)->id ); + if(vvert != NULL) + send_verse_vert_vec_real32((vec_real32_item*)item, type); + break; + case VERSE_FACE_UINT8: /* parent item has to exist */ + vnode = (((uint8_item*)item)->vlayer)->vnode; + vlayer = (VLayer*)BLI_dlist_find_link(&(((VGeomData*)vnode->data)->layers), 1 ); + vface = (VerseFace*)BLI_dlist_find_link(&(vlayer->dl), ((uint8_item*)item)->id ); + if(vface != NULL) + send_verse_face_uint8((uint8_item*)item, type); + break; + case VERSE_FACE_UINT32: /* parent item has to exist */ + vnode = (((uint32_item*)item)->vlayer)->vnode; + vlayer = (VLayer*)BLI_dlist_find_link(&(((VGeomData*)vnode->data)->layers), 1 ); + vface = (VerseFace*)BLI_dlist_find_link(&(vlayer->dl), ((uint32_item*)item)->id ); + if(vface != NULL) + send_verse_face_uint32((uint32_item*)item, type); + break; + case VERSE_FACE_REAL32: /* parent item has to exist */ + vnode = (((real32_item*)item)->vlayer)->vnode; + vlayer = (VLayer*)BLI_dlist_find_link(&(((VGeomData*)vnode->data)->layers), 1 ); + vface = (VerseFace*)BLI_dlist_find_link(&(vlayer->dl), ((real32_item*)item)->id ); + if(vface != NULL) + send_verse_face_real32((real32_item*)item, type); + break; + case VERSE_FACE_QUAT_UINT32: /* parent item has to exist */ + vnode = (((quat_uint32_item*)item)->vlayer)->vnode; + vlayer = (VLayer*)BLI_dlist_find_link(&(((VGeomData*)vnode->data)->layers), 1 ); + vface = (VerseFace*)BLI_dlist_find_link(&(vlayer->dl), ((quat_uint32_item*)item)->id ); + if(vface != NULL) + send_verse_face_corner_quat_uint32((quat_uint32_item*)item, type); + break; + case VERSE_FACE_QUAT_REAL32: /* parent item has to exist */ + vnode = (((quat_real32_item*)item)->vlayer)->vnode; + vlayer = (VLayer*)BLI_dlist_find_link(&(((VGeomData*)vnode->data)->layers), 1 ); + vface = (VerseFace*)BLI_dlist_find_link(&(vlayer->dl), ((quat_real32_item*)item)->id ); + if(vface != NULL) + send_verse_face_corner_quat_real32((quat_real32_item*)item, type); + break; + } +} + +/* + * return VerseLayer with certain content (vertexes, polygons, in the + * future: weight, red color, etc.) + */ +VLayer* find_verse_layer_type(VGeomData *geom, short content) +{ + struct VLayer *vlayer = NULL; + + switch(content) { + case VERTEX_LAYER: + /* VERTEX_LAYER equals 0 and vertex layer is + * always in 1st layer */ + vlayer = geom->layers.da.items[VERTEX_LAYER]; + break; + case POLYGON_LAYER: + /* POLYGON_LAYER equals 1 and vertex layer is + * always in 2nd layer */ + vlayer = geom->layers.da.items[POLYGON_LAYER]; + break; + } + + return vlayer; +} + +/* + * increase references of VerseVerts of new VerseFace + */ +static void increase_verse_verts_references(VerseFace *vface) +{ + if(vface->vvert0) vface->vvert0->counter++; + if(vface->vvert1) vface->vvert1->counter++; + if(vface->vvert2) vface->vvert2->counter++; + if(vface->vvert3) vface->vvert3->counter++; +} + +/* + * move VerseFace from list of orphans to dlist of VerseFaces (if VerseFace was only changed + * then this VerseFace is only removed from list of orphans) + */ +static void move_face_orphan_to_dlist(VNode *vnode, VLayer *vlayer, VerseFace *vface) +{ + /* remove vface from list of orphans */ + BLI_remlink(&(vlayer->orphans), vface); + /* increase references of all vertexes beying part of this face*/ + increase_verse_verts_references(vface); + + if(vface->flag & FACE_RECEIVED) { + /* set up vface flag */ + vface->flag &= ~FACE_RECEIVED; + /* move vface to dynamic list of faces */ + BLI_dlist_add_item_index(&(vlayer->dl), (void*)vface, vface->id); + /* recalculate all vertex and faces normals */ + recalculate_verseface_normals(vnode); + /* post create action (change local data) */ + ((VGeomData*)vnode->data)->post_polygon_create(vface); + } + else if(vface->flag & FACE_CHANGED) { + /* set up vface flag */ + vface->flag &= ~FACE_CHANGED; + /* move vface to dynamic list of faces */ + BLI_dlist_add_item_index(&(vlayer->dl), (void*)vface, vface->id); + /* recalculate all vertex and faces normals */ + recalculate_verseface_normals(vnode); + /* post create action (change local data) */ + ((VGeomData*)vnode->data)->post_polygon_set_corner(vface); + } +} + +/* + * find all VerseFaces waiting in queue, which needs id of new VerseVert + */ +static void find_unsent_faces(VNode *vnode, VerseVert *vvert) +{ + VLayer *vlayer; + VerseFace *vface, *next_vface; + + vlayer = find_verse_layer_type((VGeomData*)vnode->data, POLYGON_LAYER); + + if(vlayer) { + vface = vlayer->queue.first; + while(vface) { + next_vface = vface->next; + if(vface->vvert0==vvert) { + vface->v0 = vvert->id; + vface->counter--; + } + else if(vface->vvert1==vvert) { + vface->v1 = vvert->id; + vface->counter--; + } + else if(vface->vvert2==vvert) { + vface->v2 = vvert->id; + vface->counter--; + } + else if(vface->vvert3==vvert){ + vface->v3 = vvert->id; + vface->counter--; + } + + if(vface->counter<1 && !(vface->flag & FACE_SENT)) + send_verse_face(vface); + + vface = next_vface; + } + } +} + +/* + * find all VerseFace orphans, which needs incoming VerseVert + */ +static void find_vlayer_orphans(VNode *vnode, VerseVert *vvert) +{ + VLayer *vlayer; + VerseFace *vface, *next_vface; + unsigned int vertex_id = vvert->id; + + vlayer = find_verse_layer_type((VGeomData*)vnode->data, POLYGON_LAYER); + + if(vlayer) { + vface = vlayer->orphans.first; + while(vface){ + next_vface = vface->next; + if(vface->v0 == vertex_id) { + vface->vvert0 = vvert; + vface->counter--; + } + else if(vface->v1 == vertex_id) { + vface->vvert1 = vvert; + vface->counter--; + } + else if(vface->v2 == vertex_id) { + vface->vvert2 = vvert; + vface->counter--; + } + else if(vface->v3 == vertex_id) { + vface->vvert3 = vvert; + vface->counter--; + } + if(vface->counter<1) { + /* moving VerseFace orphan to dlist */ + move_face_orphan_to_dlist(vnode, vlayer, vface); + } + vface = next_vface; + } + } +} + +/* + * return number of VerseVerts missing to incoming VerseFace, set up pointers + * at VerseVerts + */ +static unsigned short test_incoming_verseface(VGeomData *geom, VerseFace *vface) +{ + struct VLayer *vert_layer; + struct VerseVert *vvert; + int counter=0; + + vert_layer = find_verse_layer_type(geom, VERTEX_LAYER); + + if(vface->v0 != -1){ + vvert = BLI_dlist_find_link(&(vert_layer->dl), vface->v0); + if(vvert==NULL) counter++; + else vface->vvert0 = vvert; + } + if(vface->v1 != -1){ + vvert = BLI_dlist_find_link(&(vert_layer->dl), vface->v1); + if(vvert==NULL) counter++; + else vface->vvert1 = vvert; + } + if(vface->v2 != -1){ + vvert = BLI_dlist_find_link(&(vert_layer->dl), vface->v2); + if(vvert==NULL) counter++; + else vface->vvert2 = vvert; + } + if(vface->v3 != -1){ + vvert = BLI_dlist_find_link(&(vert_layer->dl), vface->v3); + if(vvert==NULL) counter++; + else vface->vvert3 = vvert; + } + + return counter; +} + +/* + * try to find changed VerseFace in sending queue + */ +static VerseFace* find_changed_verse_face_in_queue(VLayer *vlayer, uint32 polygon_id) +{ + struct VerseFace *vface = vlayer->queue.first; + + while(vface){ + if(vface->id == polygon_id && vface->flag & FACE_CHANGED) { + return vface; + } + vface = vface->next; + } + return NULL; +} + +/* + * try to find VerseFace in queue + */ +static VerseFace* find_verse_face_in_queue( + VLayer *vlayer, + VNodeID node_id, + uint32 polygon_id, + uint32 v0, + uint32 v1, + uint32 v2, + uint32 v3) +{ + struct VerseFace *vface = vlayer->queue.first; + + while(vface){ + if((vface->v0==v0) && (vface->v1==v1) && (vface->v2==v2) && (vface->v3==v3)){ + vface->id = polygon_id; + vface->vlayer = vlayer; + return vface; + } + vface = vface->next; + } + return NULL; +} + +/* + * try to find VerseVert in queue + */ +static VerseVert* find_verse_vert_in_queue( + VLayer *vlayer, + VNodeID node_id, + uint32 vertex_id, + real32 x, + real32 y, + real32 z) +{ + struct VerseVert *vvert = vlayer->queue.first; + + while(vvert){ + if((vvert->vlayer->vnode->id == node_id) && (vvert->co[0] == x) && (vvert->co[1] == y) && (vvert->co[2] == z)) + { + vvert->id = vertex_id; + vvert->vlayer = vlayer; + + return vvert; + } + vvert = vvert->next; + } + + return NULL; +} + + +/* + * send quat of float values to verse server (4x32 bits) + */ +void send_verse_face_corner_quat_real32(quat_real32_item *item, short type) +{ + verse_send_g_polygon_set_corner_real32( + item->vlayer->vnode->id, + item->vlayer->id, + item->id, + item->value[0], + item->value[1], + item->value[2], + item->value[3]); +} + +/* + * send quat of unsigned int values to verse server (4x32 bits) + */ +void send_verse_face_corner_quat_uint32(quat_uint32_item *item, short type) +{ + verse_send_g_polygon_set_corner_uint32( + item->vlayer->vnode->id, + item->vlayer->id, + item->id, + item->value[0], + item->value[1], + item->value[2], + item->value[3]); +} + +/* + * send float value (32 bits) to verse server + */ +void send_verse_face_real32(real32_item *item, short type) +{ + verse_send_g_polygon_set_face_real32( + item->vlayer->vnode->id, + item->vlayer->id, + item->id, + item->value); +} + +/* + * send unsigned integer (32 bits) to verse server + */ +void send_verse_face_uint32(uint32_item *item, short type) +{ + verse_send_g_polygon_set_face_uint32( + item->vlayer->vnode->id, + item->vlayer->id, + item->id, + item->value); +} + +/* + * send unsigned char (8 bits) to verse server + */ +void send_verse_face_uint8(uint8_item *item, short type) +{ + verse_send_g_polygon_set_face_uint8( + item->vlayer->vnode->id, + item->vlayer->id, + item->id, + item->value); +} + +/* + * send vector of float values to verse server (3x32 bits) + */ +void send_verse_vert_vec_real32(vec_real32_item *item, short type) +{ + verse_send_g_vertex_set_xyz_real32( + item->vlayer->vnode->id, + item->vlayer->id, + item->id, + item->value[0], + item->value[1], + item->value[2]); +} + +/* + * send float value (32 bits) to verse server + */ +void send_verse_vert_real32(real32_item *item, short type) +{ + verse_send_g_vertex_set_real32( + item->vlayer->vnode->id, + item->vlayer->id, + item->id, + item->value); +} + +/* + * send unsigned integer (32 bits) to verse server + */ +void send_verse_vert_uint32(uint32_item *item, short type) +{ + verse_send_g_vertex_set_uint32( + item->vlayer->vnode->id, + item->vlayer->id, + item->id, + item->value); +} + +/* + * send delete command to verse server + */ +void send_verse_vertex_delete(VerseVert *vvert) +{ + verse_session_set(vvert->vlayer->vnode->session->vsession); + + vvert->flag |= VERT_OBSOLETE; + + verse_send_g_vertex_delete_real32(vvert->vlayer->vnode->id, vvert->id); +} + +/* + * send VerseLayer to verse server + */ +void send_verse_layer(VLayer *vlayer) +{ + verse_session_set(vlayer->vnode->session->vsession); + + verse_send_g_layer_create( + vlayer->vnode->id, + vlayer->id, + vlayer->name, + vlayer->type, + vlayer->def_int, + vlayer->def_real); +} + +/* + * send VerseVert to verse server + */ +void send_verse_vertex(VerseVert *vvert) +{ + /* new vertex position will not be sent, when vertex was deleted */ + if(vvert->flag & VERT_OBSOLETE) return; + + verse_session_set(vvert->vlayer->vnode->session->vsession); + + verse_send_g_vertex_set_xyz_real32( + vvert->vlayer->vnode->id, + vvert->vlayer->id, + vvert->id, + vvert->co[0], + vvert->co[2], + -vvert->co[1]); +} + +/* + * send delete command to verse server + */ +void send_verse_face_delete(VerseFace *vface) +{ + verse_session_set(vface->vlayer->vnode->session->vsession); + + vface->flag |= FACE_DELETED; + + verse_send_g_polygon_delete(vface->vlayer->vnode->id, vface->id); +} + +/* + * send VerseFace to verse server + */ +static void send_verse_face(VerseFace *vface) +{ + verse_session_set(vface->vlayer->vnode->session->vsession); + + vface->flag |= FACE_SENT; + + if(vface->v3 != -1) { + verse_send_g_polygon_set_corner_uint32( + vface->vlayer->vnode->id, + vface->vlayer->id, + vface->id, + vface->v0, + vface->v3, /* verse use clock-wise winding */ + vface->v2, + vface->v1); /* verse use clock-wise winding */ + } + else { + verse_send_g_polygon_set_corner_uint32( + vface->vlayer->vnode->id, + vface->vlayer->id, + vface->id, + vface->v0, + vface->v2, /* verse use clock-wise winding */ + vface->v1, /* verse use clock-wise winding */ + vface->v3); + } +} + +/* + * free VerseVert + */ +static void free_verse_vertex(VLayer *vlayer, VerseVert *vvert) +{ + /* free VerseVert */ + BLI_freelinkN(&(vlayer->orphans), vvert); +} + +/* + * free VerseFace (and blender face) + */ +static void free_verse_face(VLayer *vlayer, VerseFace *vface) +{ + /* free VerseFace */ + BLI_dlist_free_item(&(vlayer->dl), (unsigned int)vface->id); +} + +/* + * free VerseLayer data + */ +static void free_verse_layer_data(VNode *vnode, VLayer *vlayer) +{ + struct VerseFace *vface; + struct VerseVert *vvert; + + /* set up EditVert->vvert and EditFace->vface pointers to NULL */ + switch(vlayer->content) { + case VERTEX_LAYER: + vvert = (VerseVert*)vlayer->dl.lb.first; + while(vvert) { + ((VGeomData*)vnode->data)->post_vertex_free_constraint(vvert); + vvert = vvert->next; + } + break; + case POLYGON_LAYER: + vface = (VerseFace*)vlayer->dl.lb.first; + while(vface) { + ((VGeomData*)vnode->data)->post_polygon_free_constraint(vface); + vface = vface->next; + } + break; + default: + break; + } + /* free Verse Layer name */ + MEM_freeN(vlayer->name); + /* destroy VerseLayer data (vertexes, polygons, etc.) */ + BLI_dlist_destroy(&(vlayer->dl)); + /* free unsent data */ + BLI_freelistN(&(vlayer->queue)); + /* free orphans */ + BLI_freelistN(&(vlayer->orphans)); +} + +/* + * free all unneeded VerseVerts waiting for deleting + */ +static void free_unneeded_verseverts_of_verseface(VNode *vnode, VerseFace *vface) +{ + struct VLayer *vert_vlayer; + + /* find layer containing vertexes */ + vert_vlayer = find_verse_layer_type((VGeomData*)vnode->data, VERTEX_LAYER); + + /* free all "deleted" VerseVert waiting for deleting this VerseFace */ + + if((vface->vvert0->counter < 1) && (vface->vvert0->flag & VERT_DELETED)) { + ((VGeomData*)vnode->data)->post_vertex_delete(vface->vvert0); + free_verse_vertex(vert_vlayer, vface->vvert0); + vface->vvert0 = NULL; + } + if((vface->vvert1->counter < 1) && (vface->vvert1->flag & VERT_DELETED)) { + ((VGeomData*)vnode->data)->post_vertex_delete(vface->vvert1); + free_verse_vertex(vert_vlayer, vface->vvert1); + vface->vvert1 = NULL; + } + if((vface->vvert2->counter < 1) && (vface->vvert2->flag & VERT_DELETED)) { + ((VGeomData*)vnode->data)->post_vertex_delete(vface->vvert2); + free_verse_vertex(vert_vlayer, vface->vvert2); + vface->vvert2 = NULL; + } + if((vface->vvert3) && (vface->vvert3->counter < 1) && (vface->vvert3->flag & VERT_DELETED)) { + ((VGeomData*)vnode->data)->post_vertex_delete(vface->vvert3); + free_verse_vertex(vert_vlayer, vface->vvert3); + vface->vvert3 = NULL; + } +} + +/* + * This function create VerseVert and returns pointer on this vertex + */ +VerseVert* create_verse_vertex( + VLayer *vlayer, + uint32 vertex_id, + real32 x, + real32 y, + real32 z) +{ + struct VerseVert *vvert; + + vvert = (VerseVert*)MEM_mallocN(sizeof(VerseVert), "VerseVert"); + + /* set up pointer on parent node */ + vvert->vlayer = vlayer; + vvert->id = vertex_id; + /* position */ + vvert->co[0] = x; + vvert->co[1] = y; + vvert->co[2] = z; + /* normal */ + vvert->no[0] = vvert->no[1] = vvert->no[2] = 0.0; + /* blender internals */ + vvert->flag = 0; + vvert->counter = 0; + vvert->vertex = NULL; + + /* increase layer counter of vertexes */ + vlayer->counter++; + + return vvert; +} + +/* + * this function creates fake VerseEdge and returns pointer at this edge + */ +VerseEdge *create_verse_edge(uint32 v0, uint32 v1) +{ + struct VerseEdge *vedge; + + vedge = (VerseEdge*)MEM_mallocN(sizeof(VerseEdge), "VerseEdge"); + + vedge->v0 = v0; + vedge->v1 = v1; + vedge->counter = 0; + + return vedge; +} + +/* + * this function will create new VerseFace and will return pointer on such Face + */ +VerseFace* create_verse_face( + VLayer *vlayer, + uint32 polygon_id, + uint32 v0, + uint32 v1, + uint32 v2, + uint32 v3) +{ + struct VerseFace *vface; + + vface = (VerseFace*)MEM_mallocN(sizeof(VerseFace), "VerseFace"); + + /* verse data */ + vface->vlayer = vlayer; + vface->id = polygon_id; + + vface->vvert0 = NULL; + vface->vvert1 = NULL; + vface->vvert2 = NULL; + vface->vvert3 = NULL; + + vface->v0 = v0; + vface->v1 = v1; + vface->v2 = v2; + vface->v3 = v3; + + /* blender data */ + vface->face = NULL; + vface->flag = 0; + vface->counter = 4; + + /* increase layer counter of faces */ + vlayer->counter++; + + return vface; +} + +/* + * create and return VerseLayer + */ +VLayer *create_verse_layer( + VNode *vnode, + VLayerID layer_id, + const char *name, + VNGLayerType type, + uint32 def_integer, + real64 def_real) +{ + struct VLayer *vlayer; + + /* add layer to the DynamicList */ + vlayer = (VLayer*)MEM_mallocN(sizeof(VLayer), "VerseLayer"); + + /* store all relevant info to the vlayer and set up vlayer */ + vlayer->vnode = vnode; + vlayer->id = layer_id; + vlayer->name = (char*)MEM_mallocN(sizeof(char)*(sizeof(name)+1),"Verse Layer name"); + strcpy(vlayer->name, name); + vlayer->type = type; + vlayer->def_int = def_integer; + vlayer->def_real = def_real; + + if((type == VN_G_LAYER_VERTEX_XYZ) && (layer_id == 0)) + vlayer->content = VERTEX_LAYER; + else if((type == VN_G_LAYER_POLYGON_CORNER_UINT32) && (layer_id == 1)) + vlayer->content = POLYGON_LAYER; + else + vlayer->content = -1; + + /* initialize DynamicList in the vlayer (vertexes, polygons, etc.)*/ + BLI_dlist_init(&(vlayer->dl)); + /* initialization of queue of layer */ + vlayer->queue.first = vlayer->queue.last = NULL; + /* initialization of list of orphans */ + vlayer->orphans.first = vlayer->orphans.last = NULL; + /* initialize number of sent items (vertexes, faces, etc) */ + vlayer->counter = 0; + /* initialize flag */ + vlayer->flag = 0; + + /* set up methods */ + vlayer->post_layer_create = post_layer_create; + vlayer->post_layer_destroy = post_layer_destroy; + + return vlayer; +} + +/* + * create geometry data + */ +VGeomData *create_geometry_data(void) +{ + struct VGeomData *geom; + + geom = (VGeomData*)MEM_mallocN(sizeof(VGeomData), "VerseGeometryData"); + BLI_dlist_init(&(geom->layers)); + geom->vlink = NULL; + geom->queue.first = geom->queue.last = NULL; + geom->mesh = NULL; + geom->editmesh = NULL; + + /* initialize list of fake verse edges and initialize verse edge hash */ + geom->edges.first = geom->edges.last = NULL; + geom->hash = MEM_callocN(VEDHASHSIZE*sizeof(HashVerseEdge), "verse hashedge tab"); + + /* set up methods */ + geom->post_vertex_create = post_vertex_create; + geom->post_vertex_set_xyz = post_vertex_set_xyz; + geom->post_vertex_delete = post_vertex_delete; + geom->post_vertex_free_constraint = post_vertex_free_constraint; + geom->post_polygon_create = post_polygon_create; + geom->post_polygon_set_corner = post_polygon_set_corner; + geom->post_polygon_delete = post_polygon_delete; + geom->post_polygon_free_constraint = post_polygon_free_constraint; + geom->post_geometry_free_constraint = post_geometry_free_constraint; + geom->post_polygon_set_uint8 = post_polygon_set_uint8; + + return geom; +} + +/* Create item containing 4 floats */ +static quat_real32_item *create_quat_real32_item( + VLayer *vlayer, + uint32 item_id, + real32 v0, + real32 v1, + real32 v2, + real32 v3) +{ + struct quat_real32_item *item; + + item = (quat_real32_item*)MEM_mallocN(sizeof(quat_real32_item), "quat_real32_item"); + + item->vlayer = vlayer; + item->id = item_id; + item->value[0] = v0; + item->value[1] = v1; + item->value[2] = v2; + item->value[3] = v3; + + return item; +} + +/* Create item containing 1 float */ +static real32_item *create_real32_item(VLayer *vlayer, uint32 item_id, real32 value) +{ + struct real32_item *item; + + item = (real32_item*)MEM_mallocN(sizeof(real32_item), "real32_item"); + + item->vlayer = vlayer; + item->id = item_id; + item->value = value; + + return item; +} + +/* Create item containing 1 integer */ +static uint32_item *create_uint32_item(VLayer *vlayer, uint32 item_id, uint32 value) +{ + struct uint32_item *item; + + item = (uint32_item*)MEM_mallocN(sizeof(uint32_item), "uint32_item"); + + item->vlayer = vlayer; + item->id = item_id; + item->value = value; + + return item; +} + +/* Create item containing 1 byte */ +static uint8_item *create_uint8_item(VLayer *vlayer, uint32 item_id, uint8 value) +{ + struct uint8_item *item; + + item = (uint8_item*)MEM_mallocN(sizeof(uint8_item), "uint8_item"); + + item->vlayer = vlayer; + item->id = item_id; + item->value = value; + + return item; +} + +/* + * callback function: vertex crease was set + */ +static void cb_g_crease_set_vertex( + void *user_data, + VNodeID node_id, + const char *layer, + uint32 def_crease) +{ +} + +/* + * we have to test corretness of incoming data from verse server + * no two vertexes can have the same index + */ +static char test_polygon_set_corner_uint32( + uint32 v0, + uint32 v1, + uint32 v2, + uint32 v3) +{ + if((v0==v1) || (v0==v2) || (v0==v3) || (v1==v2) || (v1==v3) || (v2==v3)) + return 0; + else + return 1; +} + +/* + * try to find verse layer in sending queue of verse geometry node + */ +static VLayer *find_vlayer_in_sending_queue(VNode *vnode, VLayerID layer_id) +{ + struct VLayer *vlayer; + + /* try to find verse layyer in sending queue */ + vlayer = ((VGeomData*)vnode->data)->queue.first; + while(vlayer) { + if(vlayer->id==layer_id) return vlayer; + vlayer = vlayer->next; + } + + return NULL; +} + +/* + * this function will find edge in hash table, hash function isn't too optimal (it needs + * lot of memory for every verse node), but it works without any bug + */ +static VerseEdge* find_verse_edge(VNode *vnode, uint32 v0, uint32 v1) +{ + struct HashVerseEdge *hve; + + if(((VGeomData*)vnode->data)->hash==NULL) + ((VGeomData*)vnode->data)->hash = MEM_callocN(VEDHASHSIZE*sizeof(HashVerseEdge), "verse hashedge tab"); + + hve = ((VGeomData*)vnode->data)->hash + VEDHASH(v0, v1);; + while(hve) { + /* edge v0---v1 is the same edge as v1---v0 */ + if(hve->vedge && ((hve->vedge->v0==v0 && hve->vedge->v1==v1) || (hve->vedge->v0==v1 && hve->vedge->v1==v0))) return hve->vedge; + hve = hve->next; + } + + return NULL; +} + +/* + * insert hash of verse edge to hash table + */ +static void insert_verse_edgehash(VNode *vnode, VerseEdge *vedge) +{ + struct HashVerseEdge *first, *hve; + + if(((VGeomData*)vnode->data)->hash==NULL) + ((VGeomData*)vnode->data)->hash = MEM_callocN(VEDHASHSIZE*sizeof(HashVerseEdge), "verse hashedge tab"); + + first = ((VGeomData*)vnode->data)->hash + VEDHASH(vedge->v0, vedge->v1); + + if(first->vedge==NULL) { + first->vedge = vedge; + } + else { + hve = &(vedge->hash); + hve->vedge = vedge; + hve->next = first->next; + first->next = hve; + } +} + +/* + * remove hash of verse edge from hash table + */ +static void remove_verse_edgehash(VNode *vnode, VerseEdge *vedge) +{ + struct HashVerseEdge *first, *hve, *prev; + + hve = first = ((VGeomData*)vnode->data)->hash + VEDHASH(vedge->v0, vedge->v1); + + while(hve) { + if(hve->vedge == vedge) { + if(hve==first) { + if(first->next) { + hve = first->next; + first->vedge = hve->vedge; + first->next = hve->next; + } + else { + hve->vedge = NULL; + } + } + else { + prev->next = hve->next; + } + return; + } + prev = hve; + hve = hve->next; + } +} + +/* + * this function will try to remove existing fake verse edge, when this verse + * edge is still used by some faces, then counter will be only decremented + */ +static void remove_verse_edge(VNode *vnode, uint32 v0, uint32 v1) +{ + struct VerseEdge *vedge; + + vedge = find_verse_edge(vnode, v0, v1); + if(vedge) { + vedge->counter--; + if(vedge->counter==0) { + remove_verse_edgehash(vnode, vedge); + BLI_freelinkN(&(((VGeomData*)vnode->data)->edges), vedge); + } + } + else { + printf("error: remove_verse_edge %d, %d\n", v0, v1); + } +} + +/* + * this function will try to add new fake verse edge, when no such edge exist, + * when such edge exist, then only counter of edge will be incremented + */ +static void add_verse_edge(VNode *vnode, uint32 v0, uint32 v1) +{ + struct VerseEdge *vedge; + + vedge = find_verse_edge(vnode, v0, v1); + if(!vedge) { + if(v0!=v1) { + vedge = create_verse_edge(v0, v1); + BLI_addtail(&(((VGeomData*)vnode->data)->edges), vedge); + insert_verse_edgehash(vnode, vedge); + } + else { + printf("error:add_verse_edge: %d, %d\n", v0, v1); + return; + } + } + vedge->counter++; +} + +/* + * verse face was deleted ... update edge hash + */ +static void update_edgehash_of_deleted_verseface(VNode *vnode, VerseFace *vface) +{ + uint32 v0, v1, v2, v3; /* verse vertex indexes of deleted verse face */ + + v0 = vface->vvert0->id; + v1 = vface->vvert1->id; + v2 = vface->vvert2->id; + v3 = vface->vvert3 ? vface->vvert3->id : -1; + + remove_verse_edge(vnode, v0, v1); + remove_verse_edge(vnode, v1, v2); + if(v3!=-1) { + remove_verse_edge(vnode, v2, v3); + remove_verse_edge(vnode, v3, v0); + } + else { + remove_verse_edge(vnode, v2, v0); + } +} + +/* + * existing verse face was changed ... update edge hash + */ +static void update_edgehash_of_changed_verseface( + VNode *vnode, + VerseFace *vface, + uint32 v0, + uint32 v1, + uint32 v2, + uint32 v3) +{ + uint32 ov0, ov1, ov2, ov3; /* old indexes at verse vertexes*/ + + ov0 = vface->vvert0->id; + ov1 = vface->vvert1->id; + ov2 = vface->vvert2->id; + ov3 = vface->vvert3 ? vface->vvert3->id : -1; + + /* 1st edge */ + if(v0!=ov0 || v1!=ov1) { + remove_verse_edge(vnode, ov0, ov1); + add_verse_edge(vnode, v0, v1); + } + + /* 2nd edge */ + if(v1!=ov1 || v2!=ov2) { + remove_verse_edge(vnode, ov1, ov2); + add_verse_edge(vnode, v1, v2); + } + + /* 3rd edge */ + if(v2!=ov2 || v3!=ov3 || v0!=ov0) { + if(ov3!=-1) { + remove_verse_edge(vnode, ov2, ov3); + if(v3!=-1) { + add_verse_edge(vnode, v2, v3); /* new 3rd edge (quat->quat) */ + } + else { + remove_verse_edge(vnode, ov3, ov0); /* old edge v3,v0 of quat have to be removed */ + add_verse_edge(vnode, v2, v0); /* new 3rd edge (quat->triangle) */ + } + } + else { + remove_verse_edge(vnode, ov2, ov0); + if(v3!=-1) { + add_verse_edge(vnode, v2, v3); /* new 3rd edge (triangle->quat) */ + } + else { + add_verse_edge(vnode, v2, v0); /* new 3rd edge (triangle->triangle) */ + } + } + } + + /* 4th edge */ + if(v3!=-1 && (v3!=ov3 || v0!=ov0)) { + remove_verse_edge(vnode, ov3, ov0); + add_verse_edge(vnode, v3, v0); + } +} + +/* + * new verse face was created ... update list of edges and edge has + */ +static void update_edgehash_of_new_verseface( + VNode *vnode, + uint32 v0, + uint32 v1, + uint32 v2, + uint32 v3) +{ + /* when edge already exists, then only its counter is incremented, + * look at commentary of add_verse_edge() function */ + add_verse_edge(vnode, v0, v1); + add_verse_edge(vnode, v1, v2); + if(v3!=-1) { + add_verse_edge(vnode, v2, v3); + add_verse_edge(vnode, v3, v0); + } + else { + add_verse_edge(vnode, v2, v0); + } +} + +/* + * callback function: edge crease was set + */ +static void cb_g_crease_set_edge( + void *user_data, + VNodeID node_id, + const char *layer, + uint32 def_crease) +{ +} + +/* + * callback function: float value for polygon was set up + */ +static void cb_g_polygon_set_face_real32( + void *user_def, + VNodeID node_id, + VLayerID layer_id, + uint32 polygon_id, + real32 value) +{ + struct VerseSession *session = (VerseSession*)current_verse_session(); + struct VNode *vnode; + struct VLayer *vlayer; + struct real32_item *item; + + if(!session) return; + + /* find needed node (we can be sure, that it is geometry node) */ + vnode = (VNode*)BLI_dlist_find_link(&(session->nodes), (unsigned int)node_id); + + /* find layer containing uint_8 data */ + vlayer = (VLayer*)BLI_dlist_find_link(&(((VGeomData*)vnode->data)->layers), (unsigned int)layer_id); + + /* try to find item*/ + item = BLI_dlist_find_link(&(vlayer->dl), polygon_id); + + if(item) { + item->value = value; + } + else { + item = create_real32_item(vlayer, polygon_id, value); + BLI_dlist_add_item_index(&(vlayer->dl), item, item->id); + } +} + +/* + * callback function: int values for polygon was set up + */ +static void cb_g_polygon_set_face_uint32( + void *user_def, + VNodeID node_id, + VLayerID layer_id, + uint32 polygon_id, + uint32 value) +{ + struct VerseSession *session = (VerseSession*)current_verse_session(); + struct VNode *vnode; + struct VLayer *vlayer; + struct uint32_item *item; + + if(!session) return; + + /* find needed node (we can be sure, that it is geometry node) */ + vnode = (VNode*)BLI_dlist_find_link(&(session->nodes), (unsigned int)node_id); + + /* find layer containing uint_8 data */ + vlayer = (VLayer*)BLI_dlist_find_link(&(((VGeomData*)vnode->data)->layers), (unsigned int)layer_id); + + /* try to find item*/ + item = BLI_dlist_find_link(&(vlayer->dl), polygon_id); + + if(item) { + item->value = value; + } + else { + item = create_uint32_item(vlayer, polygon_id, value); + BLI_dlist_add_item_index(&(vlayer->dl), item, item->id); + } +} + +/* + * callback function: uint8 value for polygon was set up + */ +static void cb_g_polygon_set_face_uint8( + void *user_def, + VNodeID node_id, + VLayerID layer_id, + uint32 polygon_id, + uint8 value) +{ + struct VerseSession *session = (VerseSession*)current_verse_session(); + struct VNode *vnode; + struct VLayer *vlayer; + struct uint8_item *item; + + if(!session) return; + + /* find needed node (we can be sure, that it is geometry node) */ + vnode = (VNode*)BLI_dlist_find_link(&(session->nodes), (unsigned int)node_id); + + /* find layer containing uint_8 data */ + vlayer = (VLayer*)BLI_dlist_find_link(&(((VGeomData*)vnode->data)->layers), (unsigned int)layer_id); + + /* try to find item*/ + item = BLI_dlist_find_link(&(vlayer->dl), polygon_id); + + if(item) { + item->value = value; + } + else { + item = create_uint8_item(vlayer, polygon_id, value); + BLI_dlist_add_item_index(&(vlayer->dl), item, item->id); + } +} + +/* + * callback function: float value for polygon corner was set up + */ +static void cb_g_polygon_set_corner_real32( + void *user_def, + VNodeID node_id, + VLayerID layer_id, + uint32 polygon_id, + real32 v0, + real32 v1, + real32 v2, + real32 v3) +{ + struct VerseSession *session = (VerseSession*)current_verse_session(); + struct VNode *vnode; + struct VLayer *vlayer; + struct quat_real32_item *item; + + if(!session) return; + + /* find needed node (we can be sure, that it is geometry node) */ + vnode = (VNode*)BLI_dlist_find_link(&(session->nodes), (unsigned int)node_id); + + /* find layer containing uint_8 data */ + vlayer = (VLayer*)BLI_dlist_find_link(&(((VGeomData*)vnode->data)->layers), (unsigned int)layer_id); + + /* try to find item*/ + item = BLI_dlist_find_link(&(vlayer->dl), polygon_id); + + if(item) { + item->value[0] = v0; + item->value[1] = v1; + item->value[2] = v2; + item->value[3] = v3; + } + else { + item = create_quat_real32_item(vlayer, polygon_id, v0, v1, v2, v3); + BLI_dlist_add_item_index(&(vlayer->dl), item, item->id); + } +} + +/* + * callback function: polygon is deleted + */ +static void cb_g_polygon_delete( + void *user_data, + VNodeID node_id, + uint32 polygon_id) +{ + struct VerseSession *session = (VerseSession*)current_verse_session(); + VNode *vnode; + VLayer *vlayer; + VerseFace *vface; + + if(!session) return; + + /* find needed node (we can be sure, that it is geometry node) */ + vnode = BLI_dlist_find_link(&(session->nodes), node_id); + + /* find layer containing faces */ + vlayer = find_verse_layer_type((VGeomData*)vnode->data, POLYGON_LAYER); + + /* find wanted VerseFace */ + vface = BLI_dlist_find_link(&(vlayer->dl), polygon_id); + + if(!vface) return; + + /* update edge hash */ + update_edgehash_of_deleted_verseface(vnode, vface); + + ((VGeomData*)vnode->data)->post_polygon_delete(vface); + + /* decrease references at coresponding VerseVertexes */ + vface->vvert0->counter--; + vface->vvert1->counter--; + vface->vvert2->counter--; + if(vface->vvert3) vface->vvert3->counter--; + + /* delete unneeded VerseVertexes */ + free_unneeded_verseverts_of_verseface(vnode, vface); + + free_verse_face(vlayer, vface); +} + +/* + * callback function: new polygon (face) created or existing polygon was changed + */ +static void cb_g_polygon_set_corner_uint32( + void *user_data, + VNodeID node_id, + VLayerID layer_id, + uint32 polygon_id, + uint32 v0, + uint32 v1, + uint32 v2, + uint32 v3) +{ + struct VerseSession *session = (VerseSession*)current_verse_session(); + struct VNode *vnode; + struct VLayer *vlayer; + struct VerseFace *vface=NULL; + + if(!session) return; + + /* try to find VerseNode */ + vnode = (VNode*)BLI_dlist_find_link(&(session->nodes), (unsigned int)node_id); + if(!vnode) return; + + /* try to find VerseLayer */ + vlayer = (VLayer*)BLI_dlist_find_link(&(((VGeomData*)vnode->data)->layers), (unsigned int)layer_id); + if(!vlayer) return; + + /* we have to test coretness of incoming data */ + if(!test_polygon_set_corner_uint32(v0, v1, v2, v3)) return; + + /* Blender uses different order of vertexes */ + if(v3!=-1) { /* quat swap */ + unsigned int v; v = v1; v1 = v3; v3 = v; + } + else { /* triangle swap */ + unsigned int v; v = v1; v1 = v2; v2 = v; + } + + /* try to find VerseFace */ + vface = (VerseFace*)BLI_dlist_find_link(&(vlayer->dl), (unsigned int)polygon_id); + + /* try to find modified VerseFace */ + if(!vface) { + vface = find_changed_verse_face_in_queue(vlayer, polygon_id); + if(vface) { + BLI_remlink(&(vlayer->queue), (void*)vface); + BLI_dlist_add_item_index(&(vlayer->dl), (void*)vface, (unsigned int)polygon_id); + } + } + + if(!vface) { + /* try to find VerseFace in list of VerseVaces created by me and set up polygon and + * layer ids */ + vface = find_verse_face_in_queue(vlayer, node_id, polygon_id, v0, v1, v2, v3); + + /* update edge hash */ + update_edgehash_of_new_verseface(vnode, v0, v1, v2, v3); + + if(vface){ + /* I creeated this face ... remove VerseFace from queue */ + BLI_remlink(&(vlayer->queue), (void*)vface); + } + else { + /* some other client created this face*/ + vface = create_verse_face(vlayer, polygon_id, v0, v1, v2, v3); + } + + vface->flag &= ~FACE_SENT; + + /* return number of missing verse vertexes */ + vface->counter = test_incoming_verseface((VGeomData*)vnode->data, vface); + + if(vface->counter < 1) { + /* when VerseFace received all needed VerseFaces, then it is moved + * to list of VerseFaces */ + BLI_dlist_add_item_index(&(vlayer->dl), (void*)vface, (unsigned int)polygon_id); + increase_verse_verts_references(vface); + recalculate_verseface_normals(vnode); + ((VGeomData*)vnode->data)->post_polygon_create(vface); + } + else { + /* when all needed VerseVertexes weren't received, then VerseFace is moved to + * the list of orphans waiting on needed vertexes */ + vface->flag |= FACE_RECEIVED; + BLI_addtail(&(vlayer->orphans), (void*)vface); + } + } + else { + VLayer *vert_vlayer = find_verse_layer_type((VGeomData*)vnode->data, VERTEX_LAYER); + /* VerseVertexes of existing VerseFace were changed (VerseFace will use some different + * VerseVertexes or it will use them in different order) */ + + /* update fake verse edges */ + update_edgehash_of_changed_verseface(vnode, vface, v0, v1, v2, v3); + + /* initialize count of unreceived vertexes needed for this face */ + vface->counter = 4; + + /* 1st corner */ + if(vface->vvert0->id != v0) { + /* decrease references of obsolete vertexes*/ + vface->vvert0->counter--; + /* delete this vertex, when it isn't used by any face and it was marked as deleted */ + if((vface->vvert0->counter < 1) && (vface->vvert0->flag & VERT_DELETED)) { + ((VGeomData*)vnode->data)->post_vertex_delete(vface->vvert0); + free_verse_vertex(vert_vlayer, vface->vvert0); + } + /* try to set up new pointer at verse vertex */ + vface->v0 = v0; + vface->vvert0 = BLI_dlist_find_link(&(vert_vlayer->dl), vface->v0); + if(vface->vvert0) { + /* increase references at new vertex */ + vface->vvert0->counter++; + /* decrease count of needed vertex to receive */ + vface->counter--; + } + + } + else + /* this corner wasn't changed */ + vface->counter--; + + /* 2nd corner */ + if(vface->vvert1->id != v1) { + vface->vvert1->counter--; + if((vface->vvert1->counter < 1) && (vface->vvert1->flag & VERT_DELETED)) { + ((VGeomData*)vnode->data)->post_vertex_delete(vface->vvert1); + free_verse_vertex(vert_vlayer, vface->vvert1); + } + vface->v1 = v1; + vface->vvert1 = BLI_dlist_find_link(&(vert_vlayer->dl), vface->v1); + if(vface->vvert1) { + vface->vvert1->counter++; + vface->counter--; + } + } + else + vface->counter--; + + /* 3rd corner */ + if(vface->vvert2->id != v2) { + vface->vvert2->counter--; + if((vface->vvert2->counter < 1) && (vface->vvert2->flag & VERT_DELETED)) { + ((VGeomData*)vnode->data)->post_vertex_delete(vface->vvert2); + free_verse_vertex(vert_vlayer, vface->vvert2); + } + vface->v2 = v2; + vface->vvert2 = BLI_dlist_find_link(&(vert_vlayer->dl), vface->v2); + if(vface->vvert2) { + vface->vvert2->counter++; + vface->counter--; + } + } + else + vface->counter--; + + /* 4th corner */ + if(vface->vvert3) { + if(vface->vvert3->id != v3) { + vface->vvert3->counter--; + if((vface->vvert3->counter < 1) && (vface->vvert3->flag & VERT_DELETED)) { + ((VGeomData*)vnode->data)->post_vertex_delete(vface->vvert3); + free_verse_vertex(vert_vlayer, vface->vvert3); + } + vface->v3 = v3; + if(v3 != -1) { + vface->vvert3 = BLI_dlist_find_link(&(vert_vlayer->dl), vface->v3); + if(vface->vvert3) { + vface->vvert3->counter++; + vface->counter--; + } + } + else { + /* this is some special case, this face hase now only 3 corners + * quat -> triangle */ + vface->vvert3 = NULL; + vface->counter--; + } + } + } + else if(v3 != -1) + /* this is some special case, 4th corner of this polygon was created + * triangle -> quat */ + vface->v3 = v3; + vface->vvert3 = BLI_dlist_find_link(&(vert_vlayer->dl), vface->v3); + if(vface->vvert3) { + vface->vvert3->counter++; + vface->counter--; + } + else { + vface->v3 = -1; + vface->counter--; + } + + vface->flag &= ~FACE_SENT; + vface->flag |= FACE_CHANGED; + + if(vface->counter<1) { + vface->flag &= ~FACE_CHANGED; + recalculate_verseface_normals(vnode); + ((VGeomData*)vnode->data)->post_polygon_set_corner(vface); + } + else { + /* when all needed VerseVertexes weren't received, then VerseFace is added to + * the list of orphans waiting on needed vertexes */ + BLI_dlist_rem_item(&(vlayer->dl), vface->id); + BLI_addtail(&(vlayer->orphans), (void*)vface); + } + } +} + +/* + * callback function: float value was set up for VerseVert with vertex_id + */ +static void cb_g_vertex_set_real32( + void *user_def, + VNodeID node_id, + VLayerID layer_id, + uint32 vertex_id, + real32 value) +{ + struct VerseSession *session = (VerseSession*)current_verse_session(); + struct VNode *vnode; + struct VLayer *vlayer; + struct real32_item *item; + + if(!session) return; + + /* find needed node (we can be sure, that it is geometry node) */ + vnode = (VNode*)BLI_dlist_find_link(&(session->nodes), (unsigned int)node_id); + + /* find layer containing uint_8 data */ + vlayer = (VLayer*)BLI_dlist_find_link(&(((VGeomData*)vnode->data)->layers), (unsigned int)layer_id); + + /* try to find item*/ + item = BLI_dlist_find_link(&(vlayer->dl), vertex_id); + + if(item) { + item->value = value; + } + else { + item = create_real32_item(vlayer, vertex_id, value); + BLI_dlist_add_item_index(&(vlayer->dl), item, item->id); + } +} + +/* + * callback function: int value was set up for VerseVert with vertex_id + */ +static void cb_g_vertex_set_uint32( + void *user_def, + VNodeID node_id, + VLayerID layer_id, + uint32 vertex_id, + uint32 value) +{ + struct VerseSession *session = (VerseSession*)current_verse_session(); + struct VNode *vnode; + struct VLayer *vlayer; + struct uint32_item *item; + + if(!session) return; + + /* find needed node (we can be sure, that it is geometry node) */ + vnode = (VNode*)BLI_dlist_find_link(&(session->nodes), (unsigned int)node_id); + + /* find layer containing uint_8 data */ + vlayer = (VLayer*)BLI_dlist_find_link(&(((VGeomData*)vnode->data)->layers), (unsigned int)layer_id); + + /* try to find item*/ + item = BLI_dlist_find_link(&(vlayer->dl), vertex_id); + + if(item) { + item->value = value; + } + else { + item = create_uint32_item(vlayer, vertex_id, value); + BLI_dlist_add_item_index(&(vlayer->dl), item, item->id); + } +} + +/* + * callback function: polygon was deleted + */ +static void cb_g_vertex_delete_real32( + void *user_data, + VNodeID node_id, + uint32 vertex_id) +{ + struct VerseSession *session = (VerseSession*)current_verse_session(); + VNode *vnode=NULL; + VLayer *vert_vlayer=NULL; + VerseVert *vvert=NULL; + + if(!session) return; + + vnode = BLI_dlist_find_link(&(session->nodes), (unsigned int)node_id); + + vert_vlayer = find_verse_layer_type((VGeomData*)vnode->data, VERTEX_LAYER); + + vvert = BLI_dlist_find_link(&(vert_vlayer->dl), (unsigned int)vertex_id); + + if(!vvert) return; + + if(vvert->counter < 1) { + ((VGeomData*)vnode->data)->post_vertex_delete(vvert); + BLI_dlist_free_item(&(vert_vlayer->dl), (unsigned int)vertex_id); + } + else { + /* some VerseFace(s) still need VerseVert, remove verse vert from + * list verse vertexes and put it to list of orphans */ + vvert->flag |= VERT_DELETED; + BLI_dlist_rem_item(&(vert_vlayer->dl), (unsigned int)vertex_id); + BLI_addtail(&(vert_vlayer->orphans), vvert); + } +} + +/* + * callback function: position of one vertex was changed or new vertex was created + */ +static void cb_g_vertex_set_xyz_real32( + void *user_data, + VNodeID node_id, + VLayerID layer_id, + uint32 vertex_id, + real32 x, + real32 y, + real32 z) +{ + struct VerseSession *session = (VerseSession*)current_verse_session(); + struct VNode *vnode = NULL; + struct VLayer *vlayer = NULL; + struct VerseVert *vvert = NULL; + real32 tmp; + + if(!session) return; + + vnode = (VNode*)BLI_dlist_find_link(&(session->nodes), (unsigned int)node_id); + if(!vnode)return; + + vlayer = (VLayer*)BLI_dlist_find_link(&(((VGeomData*)vnode->data)->layers), (unsigned int)layer_id); + if(!vlayer) return; + + /* switch axis orientation */ + tmp = y; + y = -z; + z = tmp; + + if(vlayer->id == 0) { + /* try to pick up verse vert from DynamicList */ + vvert = (VerseVert*)BLI_dlist_find_link(&(vlayer->dl), (unsigned int)vertex_id); + + if(vvert) { + if(vvert->flag & VERT_OBSOLETE) return; + + if (vvert->flag & VERT_LOCKED) { + /* this application changed position of this vertex */ + if((vvert->co[0]==x) && (vvert->co[1]==y) && (vvert->co[2]==z)) { + /* unlock vertex position */ + vvert->flag &= ~VERT_LOCKED; + /* call post_vertex_set_xyz only, when position of vertex is + * obsolete ... the new vertex position will be sent to + * verse server */ + if (vvert->flag & VERT_POS_OBSOLETE) { + ((VGeomData*)vnode->data)->post_vertex_set_xyz(vvert); + } + } + } + else { + /* somebody else changed position of this vertex*/ + if((vvert->co[0]!=x) || (vvert->co[1]!=y) || (vvert->co[2]!=z)) { + vvert->co[0] = x; + vvert->co[1] = y; + vvert->co[2] = z; + recalculate_verseface_normals(vnode); + ((VGeomData*)vnode->data)->post_vertex_set_xyz(vvert); + } + } + } + else { + /* create new verse vert */ + + /* test if we are authors of this vertex :-) */ + vvert = find_verse_vert_in_queue(vlayer, node_id, vertex_id, x, y, z); + + if(vvert) { + /* remove vert from queue */ + BLI_remlink(&(vlayer->queue), (void*)vvert); + /* add vvert to the dynamic list */ + BLI_dlist_add_item_index(&(vlayer->dl), (void*)vvert, (unsigned int)vertex_id); + /* set VerseVert flags */ + vvert->flag |= VERT_RECEIVED; + if(!(vvert->flag & VERT_POS_OBSOLETE)) + vvert->flag &= ~VERT_LOCKED; + /* find VerseFaces orphans */ + find_vlayer_orphans(vnode, vvert); + /* find unsent VerseFaces */ + find_unsent_faces(vnode, vvert); + } + else { + /* create new VerseVert */ + vvert = create_verse_vertex(vlayer, vertex_id, x, y, z); + /* add VerseVert to list of VerseVerts */ + BLI_dlist_add_item_index(&(vlayer->dl), (void*)vvert, (unsigned int)vertex_id); + /* set VerseVert flags */ + vvert->flag |= VERT_RECEIVED; + /* find VerseFaces orphans */ + find_vlayer_orphans(vnode, vvert); + } + + ((VGeomData*)vnode->data)->post_vertex_create(vvert); + } + } +} + +/* + * callback function for destroyng of verse layer + */ +static void cb_g_layer_destroy( + void *user_data, + VNodeID node_id, + VLayerID layer_id) +{ + struct VerseSession *session = (VerseSession*)current_verse_session(); + struct VNode *vnode; + struct VLayer *vlayer; + + if(!session) return; + + vnode = (VNode*)BLI_dlist_find_link(&(session->nodes), node_id); + if(!vnode) return; + + vlayer = (VLayer*) BLI_dlist_find_link(&(((VGeomData*)vnode->data)->layers), layer_id); + + if(vlayer){ + /* free VerseLayer data */ + free_verse_layer_data(vnode, vlayer); + /* remove VerseLayer from list of verse layers */ + BLI_dlist_rem_item(&(((VGeomData*)vnode->data)->layers), layer_id); + /* do client dependent actions */ + vlayer->post_layer_destroy(vlayer); + /* free vlayer itself */ + MEM_freeN(vlayer); + } + +} + +/* + * callback function: new layer was created + */ +static void cb_g_layer_create( + void *user_data, + VNodeID node_id, + VLayerID layer_id, + const char *name, + VNGLayerType type, + uint32 def_integer, + real64 def_real) +{ + struct VerseSession *session = (VerseSession*)current_verse_session(); + struct VNode *vnode=NULL; + struct VLayer *vlayer=NULL; + + if(!session) return; + + /* find node of this layer*/ + vnode = BLI_dlist_find_link(&(session->nodes), node_id); + if(!vnode) return; + + /* when we created this layer, then subscribe to this layer */ + if(vnode->owner_id == VN_OWNER_MINE || session->flag & VERSE_AUTOSUBSCRIBE) + verse_send_g_layer_subscribe(node_id, layer_id, 0); + + /* try to find */ + if(vnode->owner_id == VN_OWNER_MINE) + vlayer = find_vlayer_in_sending_queue(vnode, layer_id); + + if(vlayer) { + /* remove vlayer form sending queue add verse layer to list of verse layers */ + BLI_remlink(&((VGeomData*)vnode->data)->queue, vlayer); + BLI_dlist_add_item_index(&((VGeomData*)vnode->data)->layers, (void*)vlayer, (unsigned int)vlayer->id); + /* send all not sent vertexes to verse server + * other items waiting in sending queue will be automaticaly sent to verse server, + * when verse vertexes will be received from verse server */ + if((vlayer->type == VN_G_LAYER_VERTEX_XYZ) && (layer_id==0)) { + struct VerseVert *vvert = (VerseVert*)vlayer->queue.first; + while(vvert) { + send_verse_vertex(vvert); + vvert = vvert->next; + } + } + } + else { + /* create new VerseLayer */ + vlayer = create_verse_layer(vnode, layer_id, name, type, def_integer, def_real); + /* add layer to the list of VerseLayers */ + BLI_dlist_add_item_index(&(((VGeomData*)vnode->data)->layers), (void*)vlayer, (unsigned int)layer_id); + } + + vlayer->flag |= LAYER_RECEIVED; + + /* post callback function */ + vlayer->post_layer_create(vlayer); +} + +/* + * this function will send destroy commands for all VerseVertexes and + * VerseFaces to verse server, but it will not send destroy commands + * for VerseLayers or geometry node, it can be used in other functions + * (undo, destroy geom node, some edit mesh commands, ... ), parameter of + * this function has to be geometry verse node + */ +void destroy_geometry(VNode *vnode) +{ + struct VLayer *vert_vlayer, *face_vlayer; + struct VerseFace *vface; + struct VerseVert *vvert; + + if(vnode->type != V_NT_GEOMETRY) return; + + face_vlayer = find_verse_layer_type((VGeomData*)vnode->data, POLYGON_LAYER); + vface = face_vlayer->dl.lb.first; + + while(vface) { + send_verse_face_delete(vface); + vface = vface->next; + } + + vert_vlayer = find_verse_layer_type((VGeomData*)vnode->data, VERTEX_LAYER); + vvert = vert_vlayer->dl.lb.first; + + while(vvert) { + send_verse_vertex_delete(vvert); + vvert = vvert->next; + } + + /* own destruction of local verse date will be executed, when client will + * receive apropriate callback commands from verse server */ +} + +/* + * free VGeomData + */ +void free_geom_data(VNode *vnode) +{ + struct VerseSession *session = vnode->session; + struct VLayer *vlayer; + + if(vnode->data){ + vlayer = (VLayer*)((VGeomData*)vnode->data)->layers.lb.first; + while(vlayer){ + /* unsubscribe from layer */ + if(session->flag & VERSE_CONNECTED) + verse_send_g_layer_unsubscribe(vnode->id, vlayer->id); + /* free VerseLayer data */ + free_verse_layer_data(vnode, vlayer); + /* next layer */ + vlayer = vlayer->next; + } + /* free constraint between vnode and mesh */ + ((VGeomData*)vnode->data)->post_geometry_free_constraint(vnode); + /* free all VerseLayers */ + BLI_dlist_destroy(&(((VGeomData*)vnode->data)->layers)); + /* free fake verse edges */ + BLI_freelistN(&((VGeomData*)vnode->data)->edges); + /* free edge hash */ + MEM_freeN(((VGeomData*)vnode->data)->hash); + } +} + +void set_geometry_callbacks(void) +{ + /* new layer created */ + verse_callback_set(verse_send_g_layer_create, cb_g_layer_create, NULL); + /* layer was destroyed */ + verse_callback_set(verse_send_g_layer_destroy, cb_g_layer_destroy, NULL); + + /* position of vertex was changed */ + verse_callback_set(verse_send_g_vertex_set_xyz_real32, cb_g_vertex_set_xyz_real32, NULL); + /* vertex was deleted */ + verse_callback_set(verse_send_g_vertex_delete_real32, cb_g_vertex_delete_real32, NULL); + + /* callback functions for values being associated with vertexes */ + verse_callback_set(verse_send_g_vertex_set_uint32, cb_g_vertex_set_uint32, NULL); + verse_callback_set(verse_send_g_vertex_set_real32, cb_g_vertex_set_real32, NULL); + + /* new polygon was created / vertex(es) of polygon was set */ + verse_callback_set(verse_send_g_polygon_set_corner_uint32, cb_g_polygon_set_corner_uint32, NULL); + /* polygon was deleted */ + verse_callback_set(verse_send_g_polygon_delete, cb_g_polygon_delete, NULL); + + /* callback functions for values being associated with polygon corners */ + verse_callback_set(verse_send_g_polygon_set_corner_real32, cb_g_polygon_set_corner_real32, NULL); + /* callback functions for values being associated with faces */ + verse_callback_set(verse_send_g_polygon_set_face_uint8, cb_g_polygon_set_face_uint8, NULL); + verse_callback_set(verse_send_g_polygon_set_face_uint32, cb_g_polygon_set_face_uint32, NULL); + verse_callback_set(verse_send_g_polygon_set_face_real32, cb_g_polygon_set_face_real32, NULL); + + /* crease of vertex was set */ + verse_callback_set(verse_send_g_crease_set_vertex, cb_g_crease_set_vertex, NULL); + /* crease of edge was set */ + verse_callback_set(verse_send_g_crease_set_edge, cb_g_crease_set_edge, NULL); +} + +#endif diff --git a/source/blender/blenkernel/intern/verse_method.c b/source/blender/blenkernel/intern/verse_method.c new file mode 100644 index 00000000000..89b5282acfd --- /dev/null +++ b/source/blender/blenkernel/intern/verse_method.c @@ -0,0 +1,523 @@ +/** + * $Id$ + * + * ***** BEGIN GPL/BL DUAL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. The Blender + * Foundation also sells licenses for use in proprietary software under + * the Blender License. See http://www.blender.org/BL/ for information + * about this. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Contributor(s): Nathan Letwory. + * + * ***** END GPL/BL DUAL LICENSE BLOCK ***** + */ + +#ifdef WITH_VERSE + +#include + +#include "MEM_guardedalloc.h" + +#include "DNA_listBase.h" +#include "DNA_userdef_types.h" +#include "DNA_text_types.h" + +#include "BLI_dynamiclist.h" +#include "BLI_blenlib.h" +#include "BLI_arithb.h" + +#include "BIF_verse.h" + +#include "BKE_bad_level_calls.h" +#include "BKE_library.h" +#include "BKE_text.h" +#include "BKE_verse.h" +#include "BKE_global.h" +#include "BKE_main.h" + +#include "verse.h" + +/* helper struct for creating method descriptions */ +typedef struct VMethodInfo { + const char *name; + uint8 param_count; + const VNOParamType param_type[4]; + const char *param_name[4]; + uint16 id; +} VMethodInfo; + +#ifdef VERSECHAT +/* array with methods for verse chat */ +static VMethodInfo vmethod_info[] = { + { "join", 1, { VN_O_METHOD_PTYPE_STRING }, { "channel"}}, + { "leave", 1, { VN_O_METHOD_PTYPE_STRING }, { "channel"}}, + { "hear", 3, { VN_O_METHOD_PTYPE_STRING, VN_O_METHOD_PTYPE_STRING, VN_O_METHOD_PTYPE_STRING }, { "channel", "from", "msg"}} +}; +#endif + +/* lookup a method group based on its name */ +struct VMethodGroup *lookup_vmethodgroup_name(ListBase *lb, const char *name) { + struct VMethodGroup *vmg; + + for(vmg= lb->first; vmg; vmg= vmg->next) + if(strcmp(vmg->name,name)==0) break; + + return vmg; +} + +/* lookup a method group based on its group_id */ +struct VMethodGroup *lookup_vmethodgroup(ListBase *lb, uint16 group_id) { + struct VMethodGroup *vmg; + + for(vmg= lb->first; vmg; vmg= vmg->next) + if(vmg->group_id==group_id) break; + + return vmg; +} + +/* lookup a method based on its name */ +struct VMethod *lookup_vmethod_name(ListBase *lb, const char *name) { + struct VMethod *vm; + for(vm= lb->first; vm; vm= vm->next) + if(strcmp(vm->name,name)==0) break; + + return vm; +} + +/* lookup a method based on its method_id */ +struct VMethod *lookup_vmethod(ListBase *lb, uint8 method_id) { + struct VMethod *vm; + for(vm= lb->first; vm; vm= vm->next) + if(vm->id==method_id) break; + + return vm; +} + +#ifdef VERSECHAT +/* + * send say command + */ +void send_say(const char *chan, const char *utter) +{ + struct VerseSession *session = (VerseSession*)current_verse_session(); + struct VNode *vnode; + struct VMethodGroup *vmg; + struct VMethod *vm; + VNOPackedParams *utterpack; + VNOParam args[2]; + + vnode= (VNode *)(session->nodes.lb.first); + + for( ; vnode; vnode= vnode->next) { + if(strcmp(vnode->name, "tawksrv")==0) { + vmg= lookup_vmethodgroup_name(&(vnode->methodgroups), "tawk"); + if(!vmg) break; + vm= lookup_vmethod_name(&(vmg->methods), "say"); + if(!vm) break; + args[0].vstring= (char *)chan; + args[1].vstring= (char *)utter; + if((utterpack= verse_method_call_pack(vm->param_count, vm->param_type, args))!=NULL) { + verse_send_o_method_call(vnode->id, vmg->group_id, vm->id, vnode->session->avatar, utterpack); + } + break; + } + + } +} + +/* + * send logout command + */ +void send_logout(VNode *vnode) +{ + struct VMethodGroup *vmg; + struct VMethod *vm; + VNOPackedParams *pack; + + vnode->chat_flag = CHAT_LOGGED; + vmg= lookup_vmethodgroup_name(&(vnode->methodgroups), "tawk"); + if(!vmg) return; + vm= lookup_vmethod_name(&(vmg->methods), "logout"); + if(!vm) return; + + if((pack= verse_method_call_pack(vm->param_count, vm->param_type, NULL))!=NULL) { + verse_send_o_method_call(vnode->id, vmg->group_id, vm->id, vnode->session->avatar, pack); + } + vnode->chat_flag = CHAT_NOTLOGGED; +} + +/* + * send join command + */ +void send_join(VNode *vnode, const char *chan) +{ + struct VMethodGroup *vmg; + struct VMethod *vm; + VNOPackedParams *join; + VNOParam channel[1]; + + vmg= lookup_vmethodgroup_name(&(vnode->methodgroups), "tawk"); + if(!vmg) return; + vm= lookup_vmethod_name(&(vmg->methods), "join"); + if(!vm) return; + + channel[0].vstring= (char *)chan; + if((join= verse_method_call_pack(vm->param_count, vm->param_type, channel))!=NULL) { + verse_send_o_method_call(vnode->id, vmg->group_id, vm->id, vnode->session->avatar, join); + } +} + +/* + * send leave command + */ +void send_leave(VNode *vnode, const char *chan) +{ + struct VMethodGroup *vmg; + struct VMethod *vm; + VNOPackedParams *leave; + VNOParam channel[1]; + + vmg= lookup_vmethodgroup_name(&(vnode->methodgroups), "tawk"); + if(!vmg) return; + vm= lookup_vmethod_name(&(vmg->methods), "leave"); + if(!vm) return; + + channel[0].vstring= (char *)chan; + if((leave= verse_method_call_pack(vm->param_count, vm->param_type, channel))!=NULL) { + verse_send_o_method_call(vnode->id, vmg->group_id, vm->id, vnode->session->avatar, leave); + } +} + +/* + * send login command + */ +void send_login(VNode *vnode) +{ + struct VMethodGroup *vmg; + struct VMethod *vm; + VNOPackedParams *login; + VNOParam param[1]; + + vnode->chat_flag = CHAT_LOGGED; + vmg= lookup_vmethodgroup_name(&(vnode->methodgroups), "tawk"); + if(!vmg) return; + vm= lookup_vmethod_name(&(vmg->methods), "login"); + if(!vm) return; + + param[0].vstring= U.verseuser; + + if((login= verse_method_call_pack(vm->param_count, vm->param_type, param))!=NULL) { + verse_send_o_method_call(vnode->id, vmg->group_id, vm->id, vnode->session->avatar, login); + } + vnode->chat_flag = CHAT_LOGGED; + + vnode= lookup_vnode(vnode->session, vnode->session->avatar); + vmg= lookup_vmethodgroup_name(&(vnode->methodgroups), "tawk-client"); + if(!vmg) + verse_send_o_method_group_create(vnode->session->avatar, ~0, "tawk-client"); +} +#endif + +/* + * Free a VMethod + */ +void free_verse_method(VMethod *vm) { + if(!vm) return; + + MEM_freeN(vm->param_type); +} + +/* + * Free methods for VMethodGroup + */ +void free_verse_methodgroup(VMethodGroup *vmg) +{ + struct VMethod *vm, *tmpvm; + + if(!vmg) return; + + vm= vmg->methods.first; + while(vm) { + tmpvm=vm->next; + free_verse_method(vm); + vm= tmpvm; + } + BLI_freelistN(&(vmg->methods)); +} + +/* callback for method group creation */ +static void cb_o_method_group_create( + void *user_data, + VNodeID node_id, + uint16 group_id, + const char *name) +{ + struct VerseSession *session = (VerseSession*)current_verse_session(); + struct VNode *vnode; + struct VMethodGroup *vmg; + + if(!session) return; + + vnode = BLI_dlist_find_link(&(session->nodes), (unsigned int)node_id); + + vmg = lookup_vmethodgroup(&(vnode->methodgroups), group_id); + + /* create method group holder in node node_id */ + if(!vmg) { + vmg= MEM_mallocN(sizeof(VMethodGroup), "VMethodGroup"); + vmg->group_id = group_id; + vmg->methods.first = vmg->methods.last = NULL; + BLI_addtail(&(vnode->methodgroups), vmg); + printf("new method group with name %s (group_id %d) for node %u created\n", name, group_id, node_id); + } + + /* this ensures name of an existing group gets updated, in case it is changed */ + BLI_strncpy(vmg->name, (char *)name, 16); + + /* subscribe to method group */ + verse_send_o_method_group_subscribe(node_id, group_id); + +#ifdef VERSECHAT + /* if this is our own method group, register our methods */ + if(node_id==session->avatar) { + verse_send_o_method_create(node_id, group_id, (uint8)~0u, vmethod_info[0].name, + vmethod_info[0].param_count, + (VNOParamType *)vmethod_info[0].param_type, + (const char **)vmethod_info[0].param_name); + b_verse_update(); + verse_send_o_method_create(node_id, group_id, (uint8)~0u, vmethod_info[1].name, + vmethod_info[1].param_count, + (VNOParamType *)vmethod_info[1].param_type, + (const char **)vmethod_info[1].param_name); + b_verse_update(); + verse_send_o_method_create(node_id, group_id, (uint8)~0u, vmethod_info[2].name, + vmethod_info[2].param_count, + (VNOParamType *)vmethod_info[2].param_type, + (const char **)vmethod_info[2].param_name); + b_verse_update(); + } +#endif +} + +/* callback for method group destruction */ +static void cb_o_method_group_destroy( + void *user_data, + VNodeID node_id, + uint16 group_id, + const char *name) +{ + struct VerseSession *session = (VerseSession*)current_verse_session(); + struct VNode *vnode; + struct VMethodGroup *vmg; + struct VMethod *vm; + + printf("method group %d destroyed\n", group_id); + + if(!session) return; + + vnode = BLI_dlist_find_link(&(session->nodes), (unsigned int)node_id); + for(vmg= vnode->methodgroups.first; vmg; vmg= vmg->next) + if(vmg->group_id==group_id) break; + + if(!vmg) return; /* method group doesn't exist? */ + + vmg->group_id = 0; + vmg->name[0] = '\0'; + vm= vmg->methods.first; + while(vm) { + /* free vm */ + + } + + /* TODO: unsubscribe from method group */ + BLI_remlink(&(vnode->methodgroups),vmg); + MEM_freeN(vmg); +} + +/* callback for method creation */ +static void cb_o_method_create( + void *user_data, + VNodeID node_id, + uint16 group_id, + uint16 method_id, + const char *name, + uint8 param_count, + const VNOParamType *param_type, + const char *param_name[]) +{ + struct VerseSession *session = (VerseSession*)current_verse_session(); + struct VNode *vnode; + struct VMethodGroup *vmg; + struct VMethod *vm; + unsigned int size; + unsigned int i; + char *put; + + if(!session) return; + + vnode = BLI_dlist_find_link(&(session->nodes), (unsigned int)node_id); + + vmg= lookup_vmethodgroup((&vnode->methodgroups), group_id); + + if(!vmg) return; + + vm= lookup_vmethod((&vmg->methods), method_id); + + if(!vm) { + vm= MEM_mallocN(sizeof(VMethod), "VMethod"); + vm->id= method_id; + vm->param_count= param_count; + size= param_count* (sizeof(*vm->param_type) + sizeof(*vm->param_name)); + for(i= 0; i param_type= MEM_mallocN(size, "param_type and param_name"); + memcpy(vm->param_type, param_type, sizeof(VNOParamType)*param_count); + vm->param_name= (char **)(vm->param_type + param_count); + put= (char *)(vm->param_name + param_count); + for(i= 0; i < param_count; i++) { + vm->param_name[i]= put; + strcpy(put, param_name[i]); + put += strlen(param_name[i]) + 1; + } + + BLI_addtail(&(vmg->methods), vm); +#ifdef VERSECHAT + if(strcmp(vmethod_info[0].name, name)==0) { + vmethod_info[0].id = method_id; + } +#endif + printf("method %s in group %d of node %u created\n", name, group_id, node_id); + } + + BLI_strncpy(vm->name, (char *)name, 500); +} + +/* callback for method destruction */ +static void cb_o_method_destroy( + void *user_data, + VNodeID node_id, + uint16 group_id, + uint16 method_id, + const char *name, + uint8 param_count, + const VNOParamType *param_type, + const char *param_name[]) +{ + struct VerseSession *session = (VerseSession*)current_verse_session(); + struct VNode *vnode; + struct VMethodGroup *vmg; + struct VMethod *vm; + + if(!session) return; + + vnode = BLI_dlist_find_link(&(session->nodes), (unsigned int)node_id); + for(vmg= vnode->methodgroups.first; vmg; vmg= vmg->next) + if(vmg->group_id==group_id) break; + + if(!vmg) return; /* method group doesn't exist? */ + + for(vm= vmg->methods.first; vm; vm= vm->next) + if(vm->id==method_id) break; + + if(!vm) return; + + BLI_remlink(&(vmg->methods), vm); + MEM_freeN(vm->param_type); + MEM_freeN(vm); +} + +/* callback for method calls */ +static void cb_o_method_call(void *user_data, VNodeID node_id, uint8 group_id, uint8 method_id, VNodeID sender, VNOPackedParams *params) +{ + struct VerseSession *session = (VerseSession*)current_verse_session(); + struct VNode *vnode; + struct VMethodGroup *vmg; + struct VMethod *vm; + Text *text; + int method_idx= -1; + + VNOParam arg[3]; + + if(!session) return; + + if(session->avatar!=node_id) return; + + vnode = BLI_dlist_find_link(&(session->nodes), (unsigned int)node_id); + vmg= lookup_vmethodgroup(&(vnode->methodgroups), group_id); + if(!vmg) return; + + vm= lookup_vmethod(&(vmg->methods), method_id); + if(!vm) return; +#ifdef VERSECHAT + if(strcmp(vm->name, "join")==0) method_idx=0; + if(strcmp(vm->name, "leave")==0) method_idx=1; + if(strcmp(vm->name, "hear")==0) method_idx=2; + if(method_idx>-1) + verse_method_call_unpack(params, vmethod_info[method_idx].param_count, vmethod_info[method_idx].param_type, arg); + + switch(method_idx) { + case 0: + printf("Joining channel %s\n",arg[0].vstring); + text=add_empty_text(); + text->flags |= TXT_ISCHAT; + rename_id(&(text->id), arg[0].vstring); + break; + case 1: + printf("Leaving channel %s\n",arg[0].vstring); + break; + case 2: + { + ListBase lb = G.main->text; + ID *id= (ID *)lb.first; + char showstr[1024]; + showstr[0]='\0'; + text = NULL; + sprintf(showstr, "%s: %s\n", arg[1].vstring, arg[2].vstring); + for(; id; id= id->next) { + if(strcmp(id->name+2, arg[0].vstring)==0 && strcmp(arg[0].vstring, "#server")!=0) { + text = (Text *)id; + break; + } + } + if(text) { + txt_insert_buf(text, showstr); + txt_move_eof(text, 0); + allqueue(REDRAWCHAT, 0); + } else { + printf("%s> %s: %s\n",arg[0].vstring, arg[1].vstring, arg[2].vstring); + } + } + break; + } +#endif +} + +void set_method_callbacks(void) +{ + /* create and destroy method groups */ + verse_callback_set(verse_send_o_method_group_create, cb_o_method_group_create, NULL); + verse_callback_set(verse_send_o_method_group_destroy, cb_o_method_group_destroy, NULL); + + /* create and destroy methods */ + verse_callback_set(verse_send_o_method_create, cb_o_method_create, NULL); + verse_callback_set(verse_send_o_method_destroy, cb_o_method_destroy, NULL); + + /* call methods */ + verse_callback_set(verse_send_o_method_call, cb_o_method_call, NULL); +} + +#endif diff --git a/source/blender/blenkernel/intern/verse_node.c b/source/blender/blenkernel/intern/verse_node.c new file mode 100644 index 00000000000..682ae773da5 --- /dev/null +++ b/source/blender/blenkernel/intern/verse_node.c @@ -0,0 +1,750 @@ +/** + * $Id: verse_node.c 12931 2007-12-17 18:20:48Z theeth $ + * + * ***** BEGIN GPL/BL DUAL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. The Blender + * Foundation also sells licenses for use in proprietary software under + * the Blender License. See http://www.blender.org/BL/ for information + * about this. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Contributor(s): Jiri Hnidek. + * + * ***** END GPL/BL DUAL LICENSE BLOCK ***** + */ + +#ifdef WITH_VERSE + +#include + +#include "MEM_guardedalloc.h" + +#include "DNA_listBase.h" +#include "DNA_userdef_types.h" + +#include "BLI_dynamiclist.h" +#include "BLI_blenlib.h" + +#include "BIF_verse.h" + +#include "BKE_verse.h" + +#include "verse.h" + +/* function prototypes of static functions */ + /* for tags */ +static void free_verse_tag_data(struct VTag *vtag); +static struct VTag *find_tag_in_queue(struct VTagGroup *vtaggroup, const char *name); +static struct VTag *create_verse_tag(struct VTagGroup *vtaggroup, uint16 tag_id, const char *name, VNTagType type, const VNTag *tag); + /* for verse tag groups */ +static void free_verse_taggroup_data(struct VTagGroup *taggroup); +static struct VTagGroup *find_taggroup_in_queue(struct VNode *vnode, const char *name); +static struct VTagGroup *create_verse_taggroup(VNode *vnode, uint16 group_id, const char *name); + /* for verse nodes */ +static void move_verse_node_to_dlist(struct VerseSession *session, VNodeID vnode_id); + /* function prototypes of node callback functions */ +static void cb_tag_destroy(void *user_data, VNodeID node_id, uint16 group_id, uint16 tag_id); +static void cb_tag_create(void *user_data, VNodeID node_id, uint16 group_id, uint16 tag_id, const char *name, VNTagType type, const VNTag *tag); +static void cb_tag_group_destroy(void *user_data, VNodeID node_id, uint16 group_id); +static void cb_tag_group_create(void *user_data, VNodeID node_id, uint16 group_id, const char *name); +static void cb_node_name_set(void *user_data, VNodeID node_id, const char *name); +static void cb_node_destroy(void *user_data, VNodeID node_id); +static void cb_node_create(void *user_data, VNodeID node_id, uint8 type, VNodeID owner_id); + +/* + * send new tag to verse server + */ +void send_verse_tag(VTag *vtag) +{ + verse_send_tag_create(vtag->vtaggroup->vnode->id, + vtag->vtaggroup->id, + vtag->id, + vtag->name, + vtag->type, + vtag->tag); +} + +/* + * free tag data + */ +static void free_verse_tag_data(VTag *vtag) +{ + /* free name of verse tag */ + MEM_freeN(vtag->name); + /* free value of tag */ + MEM_freeN(vtag->tag); +} + +/* + * try to find tag in sending queue ... if tag will be found, then + * this function will removed tag from queue and will return pointer + * at this tag + */ +static VTag *find_tag_in_queue(VTagGroup *vtaggroup, const char *name) +{ + struct VTag *vtag; + + vtag = vtaggroup->queue.first; + + while(vtag) { + if(strcmp(vtag->name, name)==0) { + BLI_remlink(&(vtaggroup->queue), vtag); + break; + } + vtag = vtag->next; + } + + return vtag; +} + +/* + * create new verse tag + */ +static VTag *create_verse_tag( + VTagGroup *vtaggroup, + uint16 tag_id, + const char *name, + VNTagType type, + const VNTag *tag) +{ + struct VTag *vtag; + + vtag = (VTag*)MEM_mallocN(sizeof(VTag), "VTag"); + + vtag->vtaggroup = vtaggroup; + vtag->id = tag_id; + vtag->name = (char*)MEM_mallocN(sizeof(char)*(strlen(name)+1), "VTag name"); + strcpy(vtag->name, name); + vtag->type = type; + + vtag->tag = (VNTag*)MEM_mallocN(sizeof(VNTag), "VNTag"); + *vtag->tag = *tag; + + vtag->value = NULL; + + return vtag; +} + +/* + * send taggroup to verse server + */ +void send_verse_taggroup(VTagGroup *vtaggroup) +{ + verse_send_tag_group_create( + vtaggroup->vnode->id, + vtaggroup->id, + vtaggroup->name); +} + +/* + * free taggroup data + */ +static void free_verse_taggroup_data(VTagGroup *taggroup) +{ + struct VerseSession *session = taggroup->vnode->session; + struct VTag *vtag; + + vtag = taggroup->tags.lb.first; + + while(vtag) { + free_verse_tag_data(vtag); + vtag = vtag->next; + } + + /* unsubscribe from taggroup */ + if(session->flag & VERSE_CONNECTED) + verse_send_tag_group_unsubscribe(taggroup->vnode->id, taggroup->id); + + BLI_dlist_destroy(&(taggroup->tags)); + MEM_freeN(taggroup->name); +} + +/* + * move taggroup from queue to dynamic list with access array, + * set up taggroup id and return pointer at this taggroup + */ +static VTagGroup *find_taggroup_in_queue(VNode *vnode, const char *name) +{ + struct VTagGroup *vtaggroup; + + vtaggroup = vnode->queue.first; + + while(vtaggroup) { + if(strcmp(vtaggroup->name, name)==0) { + BLI_remlink(&(vnode->queue), vtaggroup); + break; + } + vtaggroup = vtaggroup->next; + } + + return vtaggroup; +} + +/* + * create new verse group of tags + */ +static VTagGroup *create_verse_taggroup(VNode *vnode, uint16 group_id, const char *name) +{ + struct VTagGroup *taggroup; + + taggroup = (VTagGroup*)MEM_mallocN(sizeof(VTagGroup), "VTagGroup"); + + taggroup->vnode = vnode; + taggroup->id = group_id; + taggroup->name = (char*)MEM_mallocN(sizeof(char)*(strlen(name)+1), "VTagGroup name"); + strcpy(taggroup->name, name); + + BLI_dlist_init(&(taggroup->tags)); + taggroup->queue.first = taggroup->queue.last = NULL; + + taggroup->post_tag_change = post_tag_change; + taggroup->post_taggroup_create = post_taggroup_create; + + return taggroup; +} + +/* + * move first VerseNode waiting in sending queue to dynamic list of VerseNodes + * (it usually happens, when "our" VerseNode was received from verse server) + */ +static void move_verse_node_to_dlist(VerseSession *session, VNodeID vnode_id) +{ + VNode *vnode; + + vnode = session->queue.first; + + if(vnode) { + BLI_remlink(&(session->queue), vnode); + BLI_dlist_add_item_index(&(session->nodes), (void*)vnode, vnode_id); + } +} + +/* + * send VerseNode to verse server + */ +void send_verse_node(VNode *vnode) +{ + verse_send_node_create( + vnode->id, + vnode->type, + vnode->session->avatar); +} + +/* + * free Verse Node data + */ +void free_verse_node_data(VNode *vnode) +{ + struct VerseSession *session = vnode->session; + struct VTagGroup *vtaggroup; + + /* free node data (object, geometry, etc.) */ + switch(vnode->type){ + case V_NT_OBJECT: + free_object_data(vnode); + break; + case V_NT_GEOMETRY: + free_geom_data(vnode); + break; + case V_NT_BITMAP: + free_bitmap_node_data(vnode); + break; + default: + break; + } + + /* free all tag groups in dynamic list with access array */ + vtaggroup = vnode->taggroups.lb.first; + while(vtaggroup) { + free_verse_taggroup_data(vtaggroup); + vtaggroup = vtaggroup->next; + } + BLI_dlist_destroy(&(vnode->taggroups)); + + /* free all tag groups still waiting in queue */ + vtaggroup = vnode->queue.first; + while(vtaggroup) { + free_verse_taggroup_data(vtaggroup); + vtaggroup = vtaggroup->next; + } + BLI_freelistN(&(vnode->queue)); + + /* unsubscribe from node */ + if(session->flag & VERSE_CONNECTED) + verse_send_node_unsubscribe(vnode->id); + + /* free node name */ + MEM_freeN(vnode->name); + vnode->name = NULL; + + /* free node data */ + MEM_freeN(vnode->data); + vnode->data = NULL; + +} + +/* + * free VerseNode + */ +void free_verse_node(VNode *vnode) +{ + free_verse_node_data(vnode); + + BLI_dlist_free_item(&(vnode->session->nodes), vnode->id); +} + +/* + * Find a Verse Node from session + */ +VNode* lookup_vnode(VerseSession *session, VNodeID node_id) +{ + struct VNode *vnode; + vnode = BLI_dlist_find_link(&(session->nodes), (unsigned int)node_id); + + return vnode; +} + +/* + * create new Verse Node + */ +VNode* create_verse_node(VerseSession *session, VNodeID node_id, uint8 type, VNodeID owner_id) +{ + struct VNode *vnode; + + vnode = (VNode*)MEM_mallocN(sizeof(VNode), "VerseNode"); + + vnode->session = session; + vnode->id = node_id; + vnode->owner_id = owner_id; + vnode->name = NULL; + vnode->type = type; + + BLI_dlist_init(&(vnode->taggroups)); + vnode->queue.first = vnode->queue.last = NULL; + vnode->methodgroups.first = vnode->methodgroups.last = NULL; + + vnode->data = NULL; + + vnode->counter = 0; + + vnode->flag = 0; +#ifdef VERSECHAT + vnode->chat_flag = CHAT_NOTLOGGED; +#endif + + vnode->post_node_create = post_node_create; + vnode->post_node_destroy = post_node_destroy; + vnode->post_node_name_set = post_node_name_set; + + return vnode; +} + +/* + * callback function: tag was destroyed + */ +static void cb_tag_destroy( + void *user_data, + VNodeID node_id, + uint16 group_id, + uint16 tag_id) +{ + struct VerseSession *session = (VerseSession*)current_verse_session(); + struct VNode *vnode; + struct VTagGroup *vtaggroup; + struct VTag *vtag; + + if(!session) return; + + vnode = (VNode*)BLI_dlist_find_link(&(session->nodes), (unsigned int)node_id); + if(!vnode) return; + + /* try to find tag group in list of tag groups */ + vtaggroup = BLI_dlist_find_link(&(vnode->taggroups), group_id); + + if(!vtaggroup) return; + + /* try to find verse tag in dynamic list of tags in tag group */ + vtag = (VTag*)BLI_dlist_find_link(&(vtaggroup->tags), tag_id); + + if(vtag) { + free_verse_tag_data(vtag); + BLI_dlist_free_item(&(vtaggroup->tags), vtag->id); + } +} + +/* + * callback function: new tag was created + */ +static void cb_tag_create( + void *user_data, + VNodeID node_id, + uint16 group_id, + uint16 tag_id, + const char *name, + VNTagType type, + const VNTag *tag) +{ + struct VerseSession *session = (VerseSession*)current_verse_session(); + struct VNode *vnode; + struct VTagGroup *vtaggroup; + struct VTag *vtag; + + if(!session) return; + + vnode = (VNode*)BLI_dlist_find_link(&(session->nodes), (unsigned int)node_id); + if(!vnode) return; + + /* try to find tag group in list of tag groups */ + vtaggroup = BLI_dlist_find_link(&(vnode->taggroups), group_id); + + if(!vtaggroup) return; + + /* try to find verse tag in dynamic list of tags in tag group */ + vtag = (VTag*)BLI_dlist_find_link(&(vtaggroup->tags), tag_id); + + if(!vtag) { + /* we will try to find vtag in sending queue */ + vtag = find_tag_in_queue(vtaggroup, name); + + /* when we didn't create this tag, then we will have to create one */ + if(!vtag) vtag = create_verse_tag(vtaggroup, tag_id, name, type, tag); + else vtag->id = tag_id; + + /* add tag to the list of tags in tag group */ + BLI_dlist_add_item_index(&(vtaggroup->tags), vtag, tag_id); + + /* post change/create method */ + vtaggroup->post_tag_change(vtag); + } + else { + /* this tag exists, then we will propably change value of this tag */ + if((vtag->type != type) || (strcmp(vtag->name, name)!=0)) { + /* changes of type or name are not allowed and such + * stupid changes will be returned back */ + send_verse_tag(vtag); + } + else { + /* post change/create method */ + vtaggroup->post_tag_change(vtag); + } + } +} + +/* + * callback function: tag group was destroyed + */ +static void cb_tag_group_destroy( + void *user_data, + VNodeID node_id, + uint16 group_id) +{ + struct VerseSession *session = (VerseSession*)current_verse_session(); + struct VNode *vnode; + struct VTagGroup *vtaggroup; + + if(!session) return; + + vnode = (VNode*)BLI_dlist_find_link(&(session->nodes), (unsigned int)node_id); + if(!vnode) return; + + vtaggroup = BLI_dlist_find_link(&(vnode->taggroups), group_id); + + if(vtaggroup) { + free_verse_taggroup_data(vtaggroup); + BLI_dlist_free_item(&(vnode->taggroups), vtaggroup->id); + } +} + +/* + * callback function: new tag group was created + */ +static void cb_tag_group_create( + void *user_data, + VNodeID node_id, + uint16 group_id, + const char *name) +{ + struct VerseSession *session = (VerseSession*)current_verse_session(); + struct VNode *vnode; + struct VTagGroup *vtaggroup; + + if(!session) return; + + vnode = (VNode*)BLI_dlist_find_link(&(session->nodes), (unsigned int)node_id); + if(!vnode) return; + + /* name of taggroup has to begin with string "blender:" */ + if(strncmp("blender:", name, 8)) return; + + /* try to find tag group in list of tag groups */ + vtaggroup = BLI_dlist_find_link(&(vnode->taggroups), group_id); + + if(!vtaggroup) { + /* subscribe to tag group (when new tag will be created, then blender will + * receive command about it) */ + verse_send_tag_group_subscribe(vnode->id, group_id); + verse_callback_update(0); + + /* try to find taggroup in waiting queue */ + vtaggroup = find_taggroup_in_queue(vnode, name); + + /* if no taggroup exist, then new has to be created */ + if(!vtaggroup) vtaggroup = create_verse_taggroup(vnode, group_id, name); + else vtaggroup->id = group_id; + + /* add tag group to dynamic list with access array */ + BLI_dlist_add_item_index(&(vnode->taggroups), (void*)vtaggroup, (unsigned int)group_id); + + /* post create method */ + vtaggroup->post_taggroup_create(vtaggroup); + } + else { + /* this taggroup exist and somebody try to change its name */ + if(strcmp(vtaggroup->name, name)!=0) { + /* blender doesn't allow such stupid and dangerous things */ + send_verse_taggroup(vtaggroup); + } + } +} + +/* + * callback function: change name of node + */ +static void cb_node_name_set( + void *user_data, + VNodeID node_id, + const char *name) +{ + struct VerseSession *session = (VerseSession*)current_verse_session(); + struct VNode *vnode; + + if(!session) return; + + vnode = (VNode*)BLI_dlist_find_link(&(session->nodes), (unsigned int)node_id); + if(vnode && name) { + if(!vnode->name) { + vnode->name = (char*)MEM_mallocN(sizeof(char)*(strlen(name)+1), "VerseNode name"); + } + else if(strlen(name) > strlen(vnode->name)) { + MEM_freeN(vnode->name); + vnode->name = (char*)MEM_mallocN(sizeof(char)*(strlen(name)+1), "VerseNode name"); + } + strcpy(vnode->name, name); + + vnode->post_node_name_set(vnode); + } +} + +/* + * callback function for deleting node + */ +static void cb_node_destroy( + void *user_data, + VNodeID node_id) +{ + struct VerseSession *session = (VerseSession*)current_verse_session(); + struct VNode *vnode; + + if(!session) return; + + vnode = BLI_dlist_find_link(&(session->nodes), (unsigned int)node_id); + + if(vnode) { + /* remove VerseNode from dynamic list */ + BLI_dlist_rem_item(&(session->nodes), (unsigned int)node_id); + /* do post destroy operations */ + vnode->post_node_destroy(vnode); + /* free verse data */ + free_verse_node_data(vnode); + /* free VerseNode */ + MEM_freeN(vnode); + }; +} + + +/* + * callback function for new created node + */ +static void cb_node_create( + void *user_data, + VNodeID node_id, + uint8 type, + VNodeID owner_id) +{ + struct VerseSession *session = (VerseSession*)current_verse_session(); + struct VNode *vnode = NULL; + + if(!session) return; + + /* subscribe to node */ + if((type==V_NT_OBJECT) || (type==V_NT_GEOMETRY) || (type==V_NT_BITMAP)) + verse_send_node_subscribe(node_id); + else + return; + + switch(type){ + case V_NT_OBJECT : + if(owner_id==VN_OWNER_MINE) { + struct VLink *vlink; + /* collect VerseNode from VerseNode queue */ + move_verse_node_to_dlist(session, node_id); + /* send next VerseNode waiting in queue */ + if(session->queue.first) send_verse_node(session->queue.first); + /* get received VerseNode from list of VerseNodes */ + vnode = BLI_dlist_find_link(&(session->nodes), node_id); + /* set up ID */ + vnode->id = node_id; + /* set up flags */ + vnode->flag |= NODE_RECEIVED; + /* find unsent link pointing at this VerseNode */ + vlink = find_unsent_child_vlink(session, vnode); + /* send VerseLink */ + if(vlink) send_verse_link(vlink); + /* send name of object node */ + verse_send_node_name_set(node_id, vnode->name); + /* subscribe to changes of object node transformations */ + verse_send_o_transform_subscribe(node_id, 0); + /* send object transformation matrix */ + send_verse_object_position(vnode); + send_verse_object_rotation(vnode); + send_verse_object_scale(vnode); + } + else { + /* create new VerseNode */ + vnode = create_verse_node(session, node_id, type, owner_id); + /* add VerseNode to list of nodes */ + BLI_dlist_add_item_index(&(session->nodes), (void*)vnode, (unsigned int)node_id); + /* set up flags */ + vnode->flag |= NODE_RECEIVED; + /* create object data */ + vnode->data = create_object_data(); + /* set up avatar's name */ + if(node_id == session->avatar) { + verse_send_node_name_set(node_id, U.verseuser); + } + else if(session->flag & VERSE_AUTOSUBSCRIBE) { + /* subscribe to changes of object node transformations */ + verse_send_o_transform_subscribe(node_id, 0); + } + } + break; + case V_NT_GEOMETRY : + if(owner_id==VN_OWNER_MINE){ + struct VLink *vlink; + struct VLayer *vlayer; + /* collect VerseNode from VerseNode queue */ + move_verse_node_to_dlist(session, node_id); + /* send next VerseNode waiting in queue */ + if(session->queue.first) send_verse_node(session->queue.first); + /* get received VerseNode from list of VerseNodes */ + vnode = BLI_dlist_find_link(&(session->nodes), node_id); + /* set up ID */ + vnode->id = node_id; + /* set up flags */ + vnode->flag |= NODE_RECEIVED; + /* find unsent link pointing at this VerseNode */ + vlink = find_unsent_parent_vlink(session, vnode); + /* send VerseLink */ + if(vlink) send_verse_link(vlink); + /* send name of geometry node */ + verse_send_node_name_set(node_id, vnode->name); + /* send all not sent layer to verse server */ + vlayer = (VLayer*)((VGeomData*)vnode->data)->queue.first; + if(vlayer) { + while(vlayer) { + send_verse_layer(vlayer); + vlayer = vlayer->next; + } + } + else { + /* send two verse layers to verse server */ +/* verse_send_g_layer_create(node_id, 0, "vertex", VN_G_LAYER_VERTEX_XYZ, 0, 0); + verse_send_g_layer_create(node_id, 1, "polygon", VN_G_LAYER_POLYGON_CORNER_UINT32, 0, 0);*/ + } + } + else { + /* create new VerseNode*/ + vnode = create_verse_node(session, node_id, type, owner_id); + /* add VerseNode to dlist of nodes */ + BLI_dlist_add_item_index(&(session->nodes), (void*)vnode, (unsigned int)node_id); + /* set up flags */ + vnode->flag |= NODE_RECEIVED; + /* create geometry data */ + vnode->data = (void*)create_geometry_data(); + } + break; + case V_NT_BITMAP : + if(owner_id==VN_OWNER_MINE) { + /* collect VerseNode from VerseNode queue */ + move_verse_node_to_dlist(session, node_id); + /* send next VerseNode waiting in queue */ + if(session->queue.first) send_verse_node(session->queue.first); + /* get received VerseNode from list of VerseNodes */ + vnode = BLI_dlist_find_link(&(session->nodes), node_id); + /* set up ID */ + vnode->id = node_id; + /* set up flags */ + vnode->flag |= NODE_RECEIVED; + /* send name of object node */ + verse_send_node_name_set(node_id, vnode->name); + /* send dimension of image to verse server */ + verse_send_b_dimensions_set(node_id, + ((VBitmapData*)vnode->data)->width, + ((VBitmapData*)vnode->data)->height, + ((VBitmapData*)vnode->data)->depth); + } + else { + /* create new VerseNode*/ + vnode = create_verse_node(session, node_id, type, owner_id); + /* add VerseNode to dlist of nodes */ + BLI_dlist_add_item_index(&(session->nodes), (void*)vnode, (unsigned int)node_id); + /* set up flags */ + vnode->flag |= NODE_RECEIVED; + /* create bitmap data */ + vnode->data = (void*)create_bitmap_data(); + } + break; + default: + vnode = NULL; + break; + } + + if(vnode) vnode->post_node_create(vnode); +} + +/* + * set up all callbacks for verse nodes + */ +void set_node_callbacks(void) +{ + /* new node created */ + verse_callback_set(verse_send_node_create, cb_node_create, NULL); + /* node was deleted */ + verse_callback_set(verse_send_node_destroy, cb_node_destroy, NULL); + /* name of node was set */ + verse_callback_set(verse_send_node_name_set, cb_node_name_set, NULL); + + /* new tag group was created */ + verse_callback_set(verse_send_tag_group_create, cb_tag_group_create, NULL); + /* tag group was destroy */ + verse_callback_set(verse_send_tag_group_destroy, cb_tag_group_destroy, NULL); + + /* new tag was created */ + verse_callback_set(verse_send_tag_create, cb_tag_create, NULL); + /* tag was destroy */ + verse_callback_set(verse_send_tag_destroy, cb_tag_destroy, NULL); +} + +#endif diff --git a/source/blender/blenkernel/intern/verse_object_node.c b/source/blender/blenkernel/intern/verse_object_node.c new file mode 100644 index 00000000000..9f4dcc72237 --- /dev/null +++ b/source/blender/blenkernel/intern/verse_object_node.c @@ -0,0 +1,620 @@ +/** + * $Id: verse_object_node.c 12931 2007-12-17 18:20:48Z theeth $ + * + * ***** BEGIN GPL/BL DUAL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. The Blender + * Foundation also sells licenses for use in proprietary software under + * the Blender License. See http://www.blender.org/BL/ for information + * about this. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Contributor(s): Jiri Hnidek. + * + * ***** END GPL/BL DUAL LICENSE BLOCK ***** + */ + +#ifdef WITH_VERSE + +#include +#include + +#include "MEM_guardedalloc.h" + +#include "DNA_listBase.h" +#include "DNA_userdef_types.h" + +#include "BLI_dynamiclist.h" +#include "BLI_blenlib.h" +#include "BLI_arithb.h" + +#include "BIF_verse.h" + +#include "BKE_verse.h" +#include "BKE_utildefines.h" + +#include "verse.h" + +/* function prototypes of static functions */ + +/* callback functions */ +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); +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); +static void cb_o_transform_scale_real32(void *user_data, VNodeID node_id, real32 scale_x, real32 scale_y, real32 scale_z); +static void cb_o_link_set(void *user_data, VNodeID node_id, uint16 link_id, VNodeID link, const char *label, uint32 target_id); +static void cb_o_link_destroy(void *user_data, VNodeID node_id,uint16 link_id); + +/* other functions */ +static void set_target_node_link_pointer(struct VNode *vnode, struct VLink *vlink); +static void free_verse_link_data(struct VLink *vlink); + +/* + * find noy sent VerseLink in queue + */ +VLink *find_unsent_child_vlink(VerseSession *session, VNode *vnode) +{ + struct VLink *vlink; + + if(vnode->type!=V_NT_OBJECT) return NULL; + + vlink = ((VObjectData*)vnode->data)->queue.first; + while(vlink) { + if(vlink->target->id != -1) { + printf("\t vlink found, vnode target id %d\n", vlink->target->id); + return vlink; + } + vlink = vlink->next; + } + return NULL; +} + +/* + * find unsent VerseLink "pointing at this VerseNode" + */ +VLink *find_unsent_parent_vlink(VerseSession *session, VNode *vnode) +{ + struct VNode *tmp; + struct VLink *vlink; + + tmp = session->nodes.lb.first; + + while(tmp) { + if(tmp->type==V_NT_OBJECT) { + vlink = ((VObjectData*)tmp->data)->queue.first; + while(vlink) { + if(vlink->target == vnode) + return vlink; + vlink = vlink->next; + } + } + tmp = tmp->next; + } + return NULL; +} + +/* + * send object position to verse server + */ +void send_verse_object_position(VNode *vnode) +{ + float tmp; + + ((VObjectData*)vnode->data)->flag &= ~POS_SEND_READY; + + /* we have to do rotation around x axis (+pi/2) to be + compatible with other verse applications */ + tmp = -((VObjectData*)vnode->data)->pos[1]; + ((VObjectData*)vnode->data)->pos[1] = ((VObjectData*)vnode->data)->pos[2]; + ((VObjectData*)vnode->data)->pos[2] = tmp; + + verse_send_o_transform_pos_real32( + vnode->id, /* node id */ + 0, /* time_s ... no interpolation */ + 0, /* time_f ... no interpolation */ + ((VObjectData*)vnode->data)->pos, + NULL, /* speed ... no interpolation */ + NULL, /* accelerate ... no interpolation */ + NULL, /* drag normal ... no interpolation */ + 0.0); /* drag ... no interpolation */ +} + +/* + * send object rotation to verse server + */ +void send_verse_object_rotation(VNode *vnode) +{ + VNQuat32 quat; + float q[4] = {cos(-M_PI/4), -sin(-M_PI/4), 0, 0}, v[4], tmp[4]; + + /* inverse transformation to transformation in function cb_o_transform_rot_real32 */ + QuatMul(v, ((VObjectData*)vnode->data)->quat, q); + q[1]= sin(-M_PI/4); + QuatMul(tmp, q, v); + + quat.x = tmp[1]; + quat.y = tmp[2]; + quat.z = tmp[3]; + quat.w = tmp[0]; + + ((VObjectData*)vnode->data)->flag &= ~ROT_SEND_READY; + + verse_send_o_transform_rot_real32( + vnode->id, /* node id */ + 0, /* time_s ... no interpolation */ + 0, /* time_f ... no interpolation */ + &quat, + NULL, /* speed ... no interpolation */ + NULL, /* accelerate ... no interpolation */ + NULL, /* drag normal ... no interpolation */ + 0.0); /* drag ... no interpolation */ +} + +/* + * send object rotation to verse server + */ +void send_verse_object_scale(VNode *vnode) +{ + float tmp; + + ((VObjectData*)vnode->data)->flag &= ~SCALE_SEND_READY; + + /* we have to do rotation around x axis (+pi/2) to be + compatible with other verse applications */ + tmp = ((VObjectData*)vnode->data)->scale[1]; + ((VObjectData*)vnode->data)->scale[1] = ((VObjectData*)vnode->data)->scale[2]; + ((VObjectData*)vnode->data)->scale[2] = tmp; + + verse_send_o_transform_scale_real32( + vnode->id, + ((VObjectData*)vnode->data)->scale[0], + ((VObjectData*)vnode->data)->scale[1], + ((VObjectData*)vnode->data)->scale[2]); +} + +/* + * send VerseLink to verse server + */ +void send_verse_link(VLink *vlink) +{ + verse_session_set(vlink->session->vsession); + + verse_send_o_link_set( + vlink->source->id, + vlink->id, + vlink->target->id, + vlink->label, + vlink->target_id); +} + +/* + * set up pointer at VerseLink of target node (geometry node, material node, etc.) + */ +static void set_target_node_link_pointer(VNode *vnode, VLink *vlink) +{ + switch (vnode->type) { + case V_NT_GEOMETRY: + ((VGeomData*)vnode->data)->vlink = vlink; + break; + default: + break; + } +} + +/* + * free VerseLink and it's label + */ +static void free_verse_link_data(VLink *vlink) +{ + MEM_freeN(vlink->label); +} + +/* + * create new VerseLink + */ +VLink *create_verse_link( + VerseSession *session, + VNode *source, + VNode *target, + uint16 link_id, + uint32 target_id, + const char *label) +{ + struct VLink *vlink; + + vlink = (VLink*)MEM_mallocN(sizeof(VLink), "VerseLink"); + vlink->session = session; + vlink->source = source; + vlink->target = target; + vlink->id = link_id; + vlink->target_id = target_id; + + set_target_node_link_pointer(target, vlink); + + vlink->label = (char*)MEM_mallocN(sizeof(char)*(strlen(label)+1), "VerseLink label"); + vlink->label[0] = '\0'; + strcat(vlink->label, label); + + vlink->flag = 0; + + vlink->post_link_set = post_link_set; + vlink->post_link_destroy = post_link_destroy; + + return vlink; +} + +/* + * free ObjectData (links, links in queue and lables of links) + */ +void free_object_data(VNode *vnode) +{ + struct VerseSession *session = vnode->session; + struct VObjectData *obj = (VObjectData*)vnode->data; + struct VLink *vlink; + struct VMethodGroup *vmg; + + if(!obj) return; + + /* free all labels of links in dlist */ + vlink = obj->links.lb.first; + while(vlink){ + free_verse_link_data(vlink); + vlink = vlink->next; + } + + /* free all labels of links waiting in queue */ + vlink = obj->queue.first; + while(vlink){ + free_verse_link_data(vlink); + vlink = vlink->next; + } + /* free dynamic list and sendig queue of links */ + BLI_dlist_destroy(&(obj->links)); + BLI_freelistN(&(obj->queue)); + + /* free method groups and their methods */ + for(vmg = vnode->methodgroups.first; vmg; vmg= vmg->next) { + free_verse_methodgroup(vmg); + } + BLI_freelistN(&(vnode->methodgroups)); + + /* free constraint between VerseNode and Object */ + obj->post_object_free_constraint(vnode); + + /* unsubscribe from receiving changes of transformation matrix */ + if(session->flag & VERSE_CONNECTED) + verse_send_o_transform_unsubscribe(vnode->id, 0); +} + +/* + * create new object data + */ +VObjectData *create_object_data(void) +{ + VObjectData *obj; + + obj = (VObjectData*)MEM_mallocN(sizeof(VObjectData), "VerseObjectData"); + obj->object = NULL; + BLI_dlist_init(&(obj->links)); + obj->queue.first = obj->queue.last = NULL; + obj->flag = 0; + + /* transformation matrix */ + obj->pos[0] = obj->pos[1] = obj->pos[2] = 0.0; + obj->quat[0] = obj->quat[1] = obj->quat[2] = 0.0; obj->quat[3] = 1; + obj->scale[0] = obj->scale[1] = obj->scale[2] = 1.0; + + /* transformation flags */ + obj->flag |= POS_SEND_READY; + obj->flag |= ROT_SEND_READY; + obj->flag |= SCALE_SEND_READY; + + /* set up pointers at post callback functions */ +/* obj->post_transform = post_transform;*/ + obj->post_transform_pos = post_transform_pos; + obj->post_transform_rot = post_transform_rot; + obj->post_transform_scale = post_transform_scale; + obj->post_object_free_constraint = post_object_free_constraint; + + return obj; +} + +/* + * callback function: + */ +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) +{ + struct VerseSession *session = (VerseSession*)current_verse_session(); + struct VNode *vnode; + float vec[3], dt, tmp; + + if(!session) return; + + vnode = BLI_dlist_find_link(&(session->nodes), (unsigned int)node_id); + + ((VObjectData*)vnode->data)->flag |= POS_SEND_READY; + + /* verse server sends automaticaly some stupid default values ... + * we have to ignore these values, when we created this object node */ + if( (vnode->owner_id==VN_OWNER_MINE) && !(((VObjectData*)vnode->data)->flag & POS_RECEIVE_READY) ) { + ((VObjectData*)vnode->data)->flag |= POS_RECEIVE_READY; + return; + } + + dt = time_s + time_f/(0xffff); + + if(pos) { + vec[0] = pos[0]; + vec[1] = pos[1]; + vec[2] = pos[2]; + } + else { + vec[0] = 0.0f; + vec[1] = 0.0f; + vec[2] = 0.0f; + } + + if(speed) { + vec[0] += speed[0]*dt; + vec[1] += speed[1]*dt; + vec[2] += speed[2]*dt; + } + + if(accelerate) { + vec[0] += accelerate[0]*dt*dt/2; + vec[1] += accelerate[1]*dt*dt/2; + vec[2] += accelerate[2]*dt*dt/2; + } + + /* we have to do rotation around x axis (+pi/2) to be + compatible with other verse applications */ + tmp = vec[1]; + vec[1] = -vec[2]; + vec[2] = tmp; + + if( (((VObjectData*)vnode->data)->pos[0] != vec[0]) || + (((VObjectData*)vnode->data)->pos[1] != vec[1]) || + (((VObjectData*)vnode->data)->pos[2] != vec[2])) + { + ((VObjectData*)vnode->data)->pos[0] = vec[0]; + ((VObjectData*)vnode->data)->pos[1] = vec[1]; + ((VObjectData*)vnode->data)->pos[2] = vec[2]; + + ((VObjectData*)vnode->data)->post_transform_pos(vnode); + } +} + +/* + * callback function: + */ +static void cb_o_transform_rot_real32( + void *user_data, + VNodeID node_id, + uint32 time_s, + uint32 time_f, + const VNQuat32 *quat, + const VNQuat32 *speed, + const VNQuat32 *accelerate, + const VNQuat32 *drag_normal, + real32 drag) +{ + struct VerseSession *session = (VerseSession*)current_verse_session(); + struct VNode *vnode; + float temp[4]={0, 0, 0, 0}, v[4], dt; /* temporary quaternions */ + float q[4]={cos(M_PI/4), -sin(M_PI/4), 0, 0}; /* conjugate quaternion (represents rotation + around x-axis +90 degrees) */ + + if(!session) return; + + vnode = BLI_dlist_find_link(&(session->nodes), (unsigned int)node_id); + + ((VObjectData*)vnode->data)->flag |= ROT_SEND_READY; + + /* verse server sends automaticaly some stupid default values ... + * we have to ignore these values, when we created this object node */ + if( (vnode->owner_id==VN_OWNER_MINE) && !(((VObjectData*)vnode->data)->flag & ROT_RECEIVE_READY) ) { + ((VObjectData*)vnode->data)->flag |= ROT_RECEIVE_READY; + return; + } + + dt = time_s + time_f/(0xffff); + + if(quat) { + temp[1] = quat->x; + temp[2] = quat->y; + temp[3] = quat->z; + temp[0] = quat->w; + } + + if(speed) { + temp[1] += speed->x*dt; + temp[2] += speed->y*dt; + temp[3] += speed->z*dt; + temp[0] += speed->w*dt; + } + + if(accelerate) { + temp[1] += accelerate->x*dt*dt/2; + temp[2] += accelerate->y*dt*dt/2; + temp[3] += accelerate->z*dt*dt/2; + temp[0] += accelerate->w*dt*dt/2; + } + + /* following matematical operation transform rotation: + * + * v' = quaternion * v * conjugate_quaternion + * + *, where v is original representation of rotation */ + + QuatMul(v, temp, q); + q[1]= sin(M_PI/4); /* normal quaternion */ + QuatMul(temp, q, v); + + if( (((VObjectData*)vnode->data)->quat[0] != temp[0]) || + (((VObjectData*)vnode->data)->quat[1] != temp[1]) || + (((VObjectData*)vnode->data)->quat[2] != temp[2]) || + (((VObjectData*)vnode->data)->quat[3] != temp[3])) + { + QUATCOPY(((VObjectData*)vnode->data)->quat, temp); + + ((VObjectData*)vnode->data)->post_transform_rot(vnode); + } +} + +/* + * callback function: + */ +static void cb_o_transform_scale_real32( + void *user_data, + VNodeID node_id, + real32 scale_x, + real32 scale_y, + real32 scale_z) +{ + struct VerseSession *session = (VerseSession*)current_verse_session(); + struct VNode *vnode; + real32 tmp; + + if(!session) return; + + vnode = BLI_dlist_find_link(&(session->nodes), (unsigned int)node_id); + + ((VObjectData*)vnode->data)->flag |= SCALE_SEND_READY; + + /* verse server sends automaticaly some stupid default values ... + * we have to ignore these values, when we created this object node */ + if( (vnode->owner_id==VN_OWNER_MINE) && !(((VObjectData*)vnode->data)->flag & SCALE_RECEIVE_READY) ) { + ((VObjectData*)vnode->data)->flag |= SCALE_RECEIVE_READY; + return; + } + + /* flip axis (verse spec) */ + tmp = scale_y; + scale_y = scale_z; + scale_z = tmp; + + /* z and y axis are flipped here too */ + if( (((VObjectData*)vnode->data)->scale[0] != scale_x) || + (((VObjectData*)vnode->data)->scale[1] != scale_y) || + (((VObjectData*)vnode->data)->scale[2] != scale_z)) + { + ((VObjectData*)vnode->data)->scale[0] = scale_x; + ((VObjectData*)vnode->data)->scale[1] = scale_y; + ((VObjectData*)vnode->data)->scale[2] = scale_z; + + ((VObjectData*)vnode->data)->post_transform_scale(vnode); + } +} + +/* + * callback function: link between object node and some other node was created + */ +static void cb_o_link_set( + void *user_data, + VNodeID node_id, + uint16 link_id, + VNodeID link, + const char *label, + uint32 target_id) +{ + struct VLink *vlink; + struct VNode *source; + struct VNode *target; + + struct VerseSession *session = (VerseSession*)current_verse_session(); + + if(!session) return; + + source = BLI_dlist_find_link(&(session->nodes), (unsigned int)node_id); + target = BLI_dlist_find_link(&(session->nodes), (unsigned int)link); + + if(!(source && target)) return; + + vlink = ((VObjectData*)source->data)->queue.first; + + if(vlink && (vlink->source==source) && (vlink->target==target)) { + /* remove VerseLink from sending queue */ + BLI_remlink(&(((VObjectData*)source->data)->queue), vlink); + /* add VerseLink to dynamic list of VerseLinks */ + BLI_dlist_add_item_index(&(((VObjectData*)source->data)->links), vlink, (unsigned int)link_id); + /* send next link from sending queue */ + if(((VObjectData*)source->data)->queue.first) + send_verse_link(((VObjectData*)source->data)->queue.first); + /* set up VerseLink variables */ + vlink->flag = 0; + vlink->id = link_id; + vlink->target_id = target_id; + } + else { + /* create new VerseLink */ + vlink = create_verse_link(session, source, target, link_id, target_id, label); + /* add VerseLink to dynamic list of VerseLinks */ + BLI_dlist_add_item_index(&(((VObjectData*)source->data)->links), vlink, (unsigned int)link_id); + } + + target->counter++; + + vlink->post_link_set(vlink); +} + +/* + * callback function: destroy link between two VerseNodes + */ +static void cb_o_link_destroy( + void *user_data, + VNodeID node_id, + uint16 link_id) +{ + struct VerseSession *session = (VerseSession*)current_verse_session(); + struct VNode *vnode; + struct VLink *vlink; + + if(!session) return; + + vnode = BLI_dlist_find_link(&(session->nodes), (unsigned int)node_id); + + vlink = BLI_dlist_find_link(&(((VObjectData*)vnode->data)->links), link_id); + + if(vlink) { + vlink->target->counter--; + free_verse_link_data(vlink); + BLI_dlist_free_item(&(((VObjectData*)vnode->data)->links), link_id); + } + + vlink->post_link_destroy(vlink); +} + +void set_object_callbacks(void) +{ + /* position of object was changed */ + verse_callback_set(verse_send_o_transform_pos_real32, cb_o_transform_pos_real32, NULL); + /* rotation of object was changed */ + verse_callback_set(verse_send_o_transform_rot_real32, cb_o_transform_rot_real32, NULL); + /* size of object was changed */ + verse_callback_set(verse_send_o_transform_scale_real32, cb_o_transform_scale_real32, NULL); + /* new link between nodes was created */ + verse_callback_set(verse_send_o_link_set, cb_o_link_set, NULL); + /* link between nodes was destroyed */ + verse_callback_set(verse_send_o_link_destroy, cb_o_link_destroy, NULL); +} + +#endif diff --git a/source/blender/blenkernel/intern/verse_session.c b/source/blender/blenkernel/intern/verse_session.c new file mode 100644 index 00000000000..64d6b9885fe --- /dev/null +++ b/source/blender/blenkernel/intern/verse_session.c @@ -0,0 +1,480 @@ +/** + * $Id: verse_session.c 12931 2007-12-17 18:20:48Z theeth $ + * + * ***** BEGIN GPL/BL DUAL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. The Blender + * Foundation also sells licenses for use in proprietary software under + * the Blender License. See http://www.blender.org/BL/ for information + * about this. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Contributor(s): Jiri Hnidek. + * + * ***** END GPL/BL DUAL LICENSE BLOCK ***** + */ + +#ifdef WITH_VERSE + +#include + +#include "MEM_guardedalloc.h" + +#include "DNA_mesh_types.h" /* temp */ +#include "DNA_listBase.h" +#include "DNA_screen_types.h" +#include "DNA_userdef_types.h" + +#include "BLI_dynamiclist.h" +#include "BLI_blenlib.h" + +#include "BIF_screen.h" +#include "BIF_verse.h" + +#include "BKE_global.h" +#include "BKE_verse.h" + +struct ListBase session_list={NULL, NULL}; +struct ListBase server_list={NULL, NULL}; + +static int cb_ping_registered = 0; + +/* list of static function prototypes */ +static void cb_connect_terminate(const char *address, const char *bye); +static void cb_connect_accept(void *user_data, uint32 avatar, void *address, void *connection, const uint8 *host_id); +static void set_all_callbacks(void); +static void free_verse_session_data(struct VerseSession *session); +static void add_verse_server(VMSServer *server); +static void check_connection_state(struct VerseServer *server); + +static void check_connection_state(struct VerseServer *server) +{ + struct VerseSession *session; + session = session_list.first; + while(session) { + if(strcmp(server->ip,session->address)==0) { + server->flag = session->flag; + return; + } + session = session->next; + } +} +/* + * add verse server to server_list. Prevents duplicate + * entries + */ +static void add_verse_server(VMSServer *server) +{ + struct VerseServer *iter, *niter; + VerseServer *newserver; + const char *name = verse_ms_field_value(server, "DE"); + iter = server_list.first; + + while(iter) { + niter = iter->next; + if(strcmp(iter->ip, server->ip)==0) { + return; + } + iter = niter; + } + + newserver = (VerseServer *)MEM_mallocN(sizeof(VerseServer), "VerseServer"); + newserver->ip = (char *)MEM_mallocN(sizeof(char)*(strlen(server->ip)+1), "VerseServer ip"); + strcpy(newserver->ip, server->ip); + + if(name) { + newserver->name = (char *)MEM_mallocN(sizeof(char)*(strlen(name)+strlen(newserver->ip)+4), "VerseServer name"); + strcpy(newserver->name, name); + strcat(newserver->name, " ("); + strcat(newserver->name, newserver->ip); + strcat(newserver->name, ")"); + } + + newserver->flag = 0; + check_connection_state(newserver); + + printf("Adding new verse server: %s at %s\n", newserver->name, newserver->ip); + + BLI_addtail(&server_list, newserver); + post_server_add(); +} + +/* + * callback function for ping + */ +static void cb_ping(void *user, const char *address, const char *message) +{ + VMSServer **servers = verse_ms_list_parse(message); + if(servers != NULL) + { + int i; + + for(i = 0; servers[i] != NULL; i++) + add_verse_server(servers[i]); + + free(servers); + } +} + +/* + * callback function for connection terminated + */ +static void cb_connect_terminate(const char *address, const char *bye) +{ + VerseSession *session = (VerseSession*)current_verse_session(); + + if(!session) return; + + /* remove session from list of session */ + BLI_remlink(&session_list, session); + /* do post connect operations */ + session->post_connect_terminated(session); + /* free session data */ + free_verse_session_data(session); + /* free session */ + MEM_freeN(session); +} + +/* + * callback function for accepted connection to verse server + */ +static void cb_connect_accept( + void *user_data, + uint32 avatar, + void *address, + void *connection, + const uint8 *host_id) +{ + struct VerseSession *session = (VerseSession*)current_verse_session(); + struct VerseServer *server = server_list.first; + uint32 i, mask=0; + + if(!session) return; + + session->flag |= VERSE_CONNECTED; + session->flag &= ~VERSE_CONNECTING; + + while(server) { + if(strcmp(session->address, server->ip)==0) { + server->flag |= VERSE_CONNECTED; + server->flag &= ~VERSE_CONNECTING; + server->session = session; + break; + } + server = server->next; + } + + printf("\tBlender is connected to verse server: %s\n", (char*)address); + printf("\tVerseSession->counter: %d\n", session->counter); + + session->avatar = avatar; + + session->post_connect_accept(session); + + for(i = 0; i < V_NT_NUM_TYPES; i++) + mask = mask | (1 << i); + verse_send_node_index_subscribe(mask); + verse_send_node_subscribe(session->avatar); /* subscribe to avatar node, as well */ + + /* create our own method group and method */ + /*verse_send_o_method_group_create(session->avatar, ~0, "tawk-client");*/ +} + +/* + * set up all callbacks for sessions + */ +void set_verse_session_callbacks(void) +{ + /* connection */ + verse_callback_set(verse_send_connect_accept, cb_connect_accept, NULL); + /* connection was terminated */ + verse_callback_set(verse_send_connect_terminate, cb_connect_terminate, NULL); + +} + +/* + * set all callbacks used in Blender + */ +static void set_all_callbacks(void) +{ + /* set up all callbacks for sessions */ + set_verse_session_callbacks(); + + /* set up callbacks for nodes */ + set_node_callbacks(); + + /* set up all callbacks for object nodes */ + set_object_callbacks(); + + /* set up all callbacks for geometry nodes */ + set_geometry_callbacks(); + + /* set up all callbacks for bitmap nodes */ + set_bitmap_callbacks(); + + /* set up all callbacks for method groups and methods */ + set_method_callbacks(); +} + +/* + * this function sends and receive all packets for all sessions + */ +void b_verse_update(void) +{ + VerseSession *session, *next_session; + + session = session_list.first; + while(session){ + next_session = session->next; + verse_session_set(session->vsession); + if((session->flag & VERSE_CONNECTED) || (session->flag & VERSE_CONNECTING)) { + verse_callback_update(10); + session->post_connect_update(session); + } + session = next_session; + } + if(cb_ping_registered>0) { + verse_callback_update(10); + } +} + +/* + * returns VerseSession coresponding to vsession pointer + */ +VerseSession *versesession_from_vsession(VSession *vsession) +{ + struct VerseSession *session; + + session = session_list.first; + + while(session) { + if(session->vsession==vsession) return session; + session = session->next; + } + + return session; +} + +/* + * returns pointer at current VerseSession + */ +VerseSession *current_verse_session(void) +{ + struct VerseSession *session; + VSession vsession = verse_session_get(); + + session = session_list.first; + + while(session){ + if(session->vsession == vsession) + return session; + session = session->next; + } + + printf("error: non-existing SESSION occured!\n"); + return NULL; +} + +/* + * free VerseSession + */ +static void free_verse_session_data(VerseSession *session) +{ + struct VNode *vnode; + + /* free data of all nodes */ + vnode = session->nodes.lb.first; + while(vnode){ + free_verse_node_data(vnode); + vnode = vnode->next; + } + + /* free data of nodes waiting in queue */ + vnode = session->queue.first; + while(vnode){ + free_verse_node_data(vnode); + vnode = vnode->next; + } + + /* free all VerseNodes */ + BLI_dlist_destroy(&(session->nodes)); + /* free all VerseNodes waiting in queque */ + BLI_freelistN(&(session->queue)); + + /* free name of verse host for this session */ + MEM_freeN(session->address); +} + +/* + * free VerseSession + */ +void free_verse_session(VerseSession *session) +{ + /* remove session from session list*/ + BLI_remlink(&session_list, session); + /* do post terminated operations */ + session->post_connect_terminated(session); + /* free session data (nodes, layers) */ + free_verse_session_data(session); + /* free session */ + MEM_freeN(session); +} + +/* + * create new verse session and return coresponding data structure + */ +VerseSession *create_verse_session( + const char *name, + const char *pass, + const char *address, + uint8 *expected_key) +{ + struct VerseSession *session; + VSession *vsession; + + vsession = verse_send_connect(name, pass, address, expected_key); + + if(!vsession) return NULL; + + session = (VerseSession*)MEM_mallocN(sizeof(VerseSession), "VerseSession"); + + session->flag = VERSE_CONNECTING; + + session->vsession = vsession; + session->avatar = -1; + + session->address = (char*)MEM_mallocN(sizeof(char)*(strlen(address)+1),"session adress name"); + strcpy(session->address, address); + + session->connection = NULL; + session->host_id = NULL; + session->counter = 0; + + /* initialize dynamic list of nodes and node queue */ + BLI_dlist_init(&(session->nodes)); + session->queue.first = session->queue.last = NULL; + + /* set up all client dependent functions */ + session->post_connect_accept = post_connect_accept; + session->post_connect_terminated = post_connect_terminated; + session->post_connect_update = post_connect_update; + + post_server_add(); + + return session; +} + +/* + * end verse session and free all session data + */ +void end_verse_session(VerseSession *session) +{ + /* send terminate command to verse server */ + verse_send_connect_terminate(session->address, "blender: bye bye"); + /* update callbacks */ + verse_callback_update(1000); + /* send destroy session command to verse server */ + verse_session_destroy(session->vsession); + /* set up flag of verse session */ + session->flag &= ~VERSE_CONNECTED; + /* do post connect operations */ + session->post_connect_terminated(session); + /* free structure of verse session */ + free_verse_session(session); +} + +void free_all_servers(void) +{ + VerseServer *server, *nextserver; + + server = server_list.first; + + while(server) { + nextserver = server->next; + BLI_remlink(&server_list, server); + MEM_freeN(server->name); + MEM_freeN(server->ip); + MEM_freeN(server); + server = nextserver; + } + + BLI_freelistN(&server_list); +} + +/* + * end connection to all verse hosts (servers) ... free all VerseSessions + * free all VerseServers + */ +void end_all_verse_sessions(void) +{ + VerseSession *session,*nextsession; + + session = session_list.first; + + while(session) { + nextsession= session->next; + end_verse_session(session); + /* end next session */ + session = nextsession; + } + + BLI_freelistN(&session_list); + + free_all_servers(); +} + +/* + * do a get from ms + */ +void b_verse_ms_get(void) +{ + if(cb_ping_registered==0) { + /* handle ping messages (for master server) */ + verse_callback_set(verse_send_ping, cb_ping, NULL); + add_screenhandler(G.curscreen, SCREEN_HANDLER_VERSE, 1); + cb_ping_registered++; + } + free_all_servers(); + + verse_ms_get_send(U.versemaster, VERSE_MS_FIELD_DESCRIPTION, NULL); + verse_callback_update(10); +} + +/* + * connect to verse host, set up all callbacks, create session + */ +void b_verse_connect(char *address) +{ + VerseSession *session = NULL; + + /* if no session was created before, then set up all callbacks */ + if((session_list.first==NULL) && (session_list.last==NULL)) + set_all_callbacks(); + + /* create new session */ + if(address) + session = create_verse_session("Blender", "pass", address, NULL); + + if(session) { + /* add new session to the list of sessions */ + BLI_addtail(&session_list, session); + + /* add verse handler if this is first session */ + if(session_list.first == session_list.last) + add_screenhandler(G.curscreen, SCREEN_HANDLER_VERSE, 1); + + } +} + +#endif diff --git a/source/blender/editors/include/ED_particle.h b/source/blender/editors/include/ED_particle.h new file mode 100644 index 00000000000..b20db0cb7e8 --- /dev/null +++ b/source/blender/editors/include/ED_particle.h @@ -0,0 +1,99 @@ +/* + * $Id: ED_editparticle.h $ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2007 by Janne Karhu. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef ED_PARTICLE_H +#define ED_PARTICLE_H + +struct Object; +struct ParticleSystem; +struct ParticleEditSettings; +struct RadialControl; +struct ViewContext; +struct rcti; +struct wmWindowManager; + +/* particle edit mode */ +void PE_set_particle_edit(struct Scene *scene); +void PE_create_particle_edit(struct Scene *scene, struct Object *ob, struct ParticleSystem *psys); +void PE_free_particle_edit(struct ParticleSystem *psys); + +void PE_change_act(void *ob_v, void *act_v); +void PE_change_act_psys(struct Scene *scene, struct Object *ob, struct ParticleSystem *psys); +int PE_can_edit(struct ParticleSystem *psys); + +/* access */ +struct ParticleSystem *PE_get_current(struct Scene *scene, struct Object *ob); +short PE_get_current_num(struct Object *ob); +int PE_minmax(struct Scene *scene, float *min, float *max); +void PE_get_colors(char sel[4], char nosel[4]); +struct ParticleEditSettings *PE_settings(Scene *scene); +struct RadialControl **PE_radialcontrol(void); + +/* update calls */ +void PE_hide_keys_time(struct Scene *scene, struct ParticleSystem *psys, float cfra); +void PE_update_object(struct Scene *scene, struct Object *ob, int useflag); +void PE_update_selection(struct Scene *scene, struct Object *ob, int useflag); +void PE_recalc_world_cos(struct Object *ob, struct ParticleSystem *psys); + +/* selection tools */ +void PE_select_root(void); +void PE_select_tip(void); +void PE_deselectall(void); +void PE_select_linked(void); +void PE_select_less(void); +void PE_select_more(void); + +void PE_mouse_particles(void); +void PE_border_select(struct ViewContext *vc, struct rcti *rect, int select); +void PE_circle_select(struct ViewContext *vc, int selecting, short *mval, float rad); +void PE_lasso_select(struct ViewContext *vc, short mcords[][2], short moves, short select); + +/* tools */ +void PE_hide(int mode); +void PE_rekey(void); +void PE_subdivide(Object *ob); +int PE_brush_particles(void); +void PE_remove_doubles(void); +void PE_selectbrush_menu(Scene *scene); +void PE_remove_doubles(void); +void PE_radialcontrol_start(const int mode); + +/* undo */ +void PE_undo_push(Scene *scene, char *str); +void PE_undo_step(Scene *scene, int step); +void PE_undo(Scene *scene); +void PE_redo(Scene *scene); +void PE_undo_menu(Scene *scene, Object *ob); + +/* operators */ +void ED_operatortypes_particle(void); +void ED_keymap_particle(struct wmWindowManager *wm); + +#endif /* ED_PARTICLE_H */ + diff --git a/source/blender/editors/space_graph/graph_buttons.c b/source/blender/editors/space_graph/graph_buttons.c new file mode 100644 index 00000000000..577ff79c402 --- /dev/null +++ b/source/blender/editors/space_graph/graph_buttons.c @@ -0,0 +1,213 @@ +/** + * $Id: + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2009 Blender Foundation. + * All rights reserved. + * + * + * Contributor(s): Blender Foundation, Joshua Leung + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include +#include +#include +#include + +#include "DNA_anim_types.h" +#include "DNA_action_types.h" +#include "DNA_object_types.h" +#include "DNA_space_types.h" +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" +#include "DNA_userdef_types.h" + +#include "MEM_guardedalloc.h" + +#include "BLI_arithb.h" +#include "BLI_blenlib.h" +#include "BLI_editVert.h" +#include "BLI_rand.h" + +#include "BKE_animsys.h" +#include "BKE_action.h" +#include "BKE_context.h" +#include "BKE_curve.h" +#include "BKE_customdata.h" +#include "BKE_depsgraph.h" +#include "BKE_object.h" +#include "BKE_global.h" +#include "BKE_scene.h" +#include "BKE_screen.h" +#include "BKE_utildefines.h" + +#include "BIF_gl.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "ED_anim_api.h" +#include "ED_keyframing.h" +#include "ED_screen.h" +#include "ED_types.h" +#include "ED_util.h" + +#include "UI_interface.h" +#include "UI_resources.h" +#include "UI_view2d.h" + +#include "graph_intern.h" // own include + + +/* ******************* view3d space & buttons ************** */ +#define B_NOP 1 +#define B_REDR 2 + +static void do_graph_region_buttons(bContext *C, void *arg, int event) +{ + //Scene *scene= CTX_data_scene(C); + + switch(event) { + + } + + /* default for now */ + //WM_event_add_notifier(C, NC_OBJECT|ND_TRANSFORM, ob); +} + + +static void graph_panel_properties(const bContext *C, ARegion *ar, short cntrl, bAnimListElem *ale) // GRAPH_HANDLER_SETTINGS +{ + uiBlock *block; + char name[128]; + + block= uiBeginBlock(C, ar, "graph_panel_properties", UI_EMBOSS, UI_HELV); + if(uiNewPanel(C, ar, block, "Properties", "Graph", 340, 30, 318, 254)==0) return; + uiBlockSetHandleFunc(block, do_graph_region_buttons, NULL); + + /* to force height */ + uiNewPanelHeight(block, 204); + + // XXX testing buttons + uiDefBut(block, LABEL, 1, "Active F-Curve:", 10, 200, 150, 19, NULL, 0.0, 0.0, 0, 0, ""); + + getname_anim_fcurve(name, ale->id, (FCurve *)ale->data); + uiDefBut(block, LABEL, 1, name, 30, 180, 300, 19, NULL, 0.0, 0.0, 0, 0, "Name of Active F-Curve"); + +#if 0 + uiBlockBeginAlign(block); + uiDefButF(block, NUM, B_REDR, "Spacing:", 10, 200, 140, 19, &v3d->grid, 0.001, 100.0, 10, 0, "Set the distance between grid lines"); + uiDefButS(block, NUM, B_REDR, "Lines:", 10, 180, 140, 19, &v3d->gridlines, 0.0, 100.0, 100, 0, "Set the number of grid lines in perspective view"); + uiDefButS(block, NUM, B_REDR, "Divisions:", 10, 160, 140, 19, &v3d->gridsubdiv, 1.0, 100.0, 100, 0, "Set the number of grid lines"); + uiBlockEndAlign(block); +#endif +} + +/* Find 'active' F-Curve. It must be editable, since that's the purpose of these buttons (subject to change). + * We return the 'wrapper' since it contains valuable context info (about hierarchy), which will need to be freed + * when the caller is done with it. + */ +// TODO: move this to anim api with another name? +static bAnimListElem *get_active_fcurve_channel (bAnimContext *ac) +{ + ListBase anim_data = {NULL, NULL}; + int filter= (ANIMFILTER_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_ACTIVE | ANIMFILTER_CURVESONLY); + int items = ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); + + /* We take the first F-Curve only, since some other ones may have had 'active' flag set + * if they were from linked data. + */ + if (items) { + bAnimListElem *ale= (bAnimListElem *)anim_data.first; + + /* remove first item from list, then free the rest of the list and return the stored one */ + BLI_remlink(&anim_data, ale); + BLI_freelistN(&anim_data); + + return ale; + } + + /* no active F-Curve */ + return NULL; +} + +void graph_region_buttons(const bContext *C, ARegion *ar) +{ + SpaceIpo *sipo= (SpaceIpo *)CTX_wm_space_data(C); + bAnimContext ac; + bAnimListElem *ale= NULL; + + /* for now, only draw if we could init the anim-context info (necessary for all animation-related tools) + * to work correctly is able to be correctly retrieved. There's no point showing empty panels? + */ + if (ANIM_animdata_get_context(C, &ac) == 0) + return; + + + /* try to find 'active' F-Curve */ + ale= get_active_fcurve_channel(&ac); + if (ale == NULL) + return; + + // XXX temp panel for testing + graph_panel_properties(C, ar, 0, ale); + + /* driver settings for active F-Curve (only for 'Drivers' mode) */ + if (sipo->mode == SIPO_MODE_DRIVERS) { + //graph_panel_drivers(C, ar, 0); + } + + uiDrawPanels(C, 1); /* 1 = align */ + uiMatchPanelsView2d(ar); /* sets v2d->totrct */ + + /* free temp data */ + MEM_freeN(ale); +} + + +static int graph_properties(bContext *C, wmOperator *op) +{ + ScrArea *sa= CTX_wm_area(C); + ARegion *ar= graph_has_buttons_region(sa); + + if(ar) { + ar->flag ^= RGN_FLAG_HIDDEN; + ar->v2d.flag &= ~V2D_IS_INITIALISED; /* XXX should become hide/unhide api? */ + + ED_area_initialize(CTX_wm_manager(C), CTX_wm_window(C), sa); + ED_area_tag_redraw(sa); + } + return OPERATOR_FINISHED; +} + +void GRAPHEDIT_OT_properties(wmOperatorType *ot) +{ + ot->name= "Properties"; + ot->idname= "GRAPHEDIT_OT_properties"; + + ot->exec= graph_properties; + ot->poll= ED_operator_ipo_active; // xxx + + /* flags */ + ot->flag= 0; +} diff --git a/source/blender/editors/space_view3d/view3d_snap.c b/source/blender/editors/space_view3d/view3d_snap.c new file mode 100644 index 00000000000..a74e24c93f3 --- /dev/null +++ b/source/blender/editors/space_view3d/view3d_snap.c @@ -0,0 +1,1148 @@ +/** + * $Id: view3d_snap.c 18967 2009-02-14 13:07:09Z ton $ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include +#include + +#include "MEM_guardedalloc.h" + +#include "DNA_action_types.h" +#include "DNA_armature_types.h" +#include "DNA_curve_types.h" +#include "DNA_group_types.h" +#include "DNA_ipo_types.h" +#include "DNA_lattice_types.h" +#include "DNA_meta_types.h" +#include "DNA_mesh_types.h" +#include "DNA_modifier_types.h" +#include "DNA_object_types.h" +#include "DNA_screen_types.h" +#include "DNA_scene_types.h" +#include "DNA_space_types.h" +#include "DNA_view3d_types.h" + +#include "BLI_blenlib.h" +#include "BLI_arithb.h" +#include "BLI_editVert.h" +#include "BLI_linklist.h" + +#include "BKE_action.h" +#include "BKE_anim.h" +#include "BKE_context.h" +#include "BKE_armature.h" +#include "BKE_curve.h" +#include "BKE_depsgraph.h" +#include "BKE_DerivedMesh.h" +#include "BKE_displist.h" +#include "BKE_global.h" +#include "BKE_lattice.h" +#include "BKE_mesh.h" +#include "BKE_modifier.h" +#include "BKE_object.h" +#include "BKE_utildefines.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "UI_interface.h" + +#include "ED_anim_api.h" +#include "ED_armature.h" +#include "ED_mesh.h" +#include "ED_screen.h" +#include "ED_view3d.h" + +#include "view3d_intern.h" + + +/* ************************************************** */ +/* ********************* old transform stuff ******** */ +/* *********** will get replaced with new transform * */ +/* ************************************************** */ + +typedef struct TransVert { + float *loc; + float oldloc[3], fac; + float *val, oldval; + int flag; + float *nor; +} TransVert; + +static TransVert *transvmain=NULL; +static int tottrans= 0; + +/* copied from editobject.c, now uses (almost) proper depgraph */ +static void special_transvert_update(Scene *scene, Object *obedit) +{ + + if(obedit) { + + DAG_object_flush_update(scene, obedit, OB_RECALC_DATA); + + if(obedit->type==OB_MESH) { + Mesh *me= obedit->data; + recalc_editnormals(me->edit_mesh); // does face centers too + } + else if (ELEM(obedit->type, OB_CURVE, OB_SURF)) { + Curve *cu= obedit->data; + Nurb *nu= cu->editnurb->first; + + while(nu) { + test2DNurb(nu); + testhandlesNurb(nu); /* test for bezier too */ + nu= nu->next; + } + } + else if(obedit->type==OB_ARMATURE){ + bArmature *arm= obedit->data; + EditBone *ebo; + TransVert *tv= transvmain; + int a=0; + + /* Ensure all bone tails are correctly adjusted */ + for (ebo= arm->edbo->first; ebo; ebo=ebo->next) { + /* adjust tip if both ends selected */ + if ((ebo->flag & BONE_ROOTSEL) && (ebo->flag & BONE_TIPSEL)) { + if (tv) { + float diffvec[3]; + + VecSubf(diffvec, tv->loc, tv->oldloc); + VecAddf(ebo->tail, ebo->tail, diffvec); + + a++; + if (aedbo->first; ebo; ebo=ebo->next) { + if ((ebo->flag & BONE_CONNECTED) && ebo->parent){ + /* If this bone has a parent tip that has been moved */ + if (ebo->parent->flag & BONE_TIPSEL){ + VECCOPY (ebo->head, ebo->parent->tail); + } + /* If this bone has a parent tip that has NOT been moved */ + else{ + VECCOPY (ebo->parent->tail, ebo->head); + } + } + } + if(arm->flag & ARM_MIRROR_EDIT) + transform_armature_mirror_update(obedit); + } + else if(obedit->type==OB_LATTICE) { + Lattice *lt= obedit->data; + + if(lt->editlatt->flag & LT_OUTSIDE) + outside_lattice(lt->editlatt); + } + } +} + +/* copied from editobject.c, needs to be replaced with new transform code still */ +/* mode: 1 = proportional, 2 = all joints (for bones only) */ +static void make_trans_verts(Object *obedit, float *min, float *max, int mode) +{ + Nurb *nu; + BezTriple *bezt; + BPoint *bp; + TransVert *tv=NULL; + MetaElem *ml; + EditVert *eve; + EditBone *ebo; + float total, center[3], centroid[3]; + int a; + + tottrans= 0; // global! + + INIT_MINMAX(min, max); + centroid[0]=centroid[1]=centroid[2]= 0.0; + + if(obedit->type==OB_MESH) { + Mesh *me= obedit->data; + EditMesh *em= me->edit_mesh; + int proptrans= 0; + + // transform now requires awareness for select mode, so we tag the f1 flags in verts + tottrans= 0; + if(em->selectmode & SCE_SELECT_VERTEX) { + for(eve= em->verts.first; eve; eve= eve->next) { + if(eve->h==0 && (eve->f & SELECT)) { + eve->f1= SELECT; + tottrans++; + } + else eve->f1= 0; + } + } + else if(em->selectmode & SCE_SELECT_EDGE) { + EditEdge *eed; + for(eve= em->verts.first; eve; eve= eve->next) eve->f1= 0; + for(eed= em->edges.first; eed; eed= eed->next) { + if(eed->h==0 && (eed->f & SELECT)) eed->v1->f1= eed->v2->f1= SELECT; + } + for(eve= em->verts.first; eve; eve= eve->next) if(eve->f1) tottrans++; + } + else { + EditFace *efa; + for(eve= em->verts.first; eve; eve= eve->next) eve->f1= 0; + for(efa= em->faces.first; efa; efa= efa->next) { + if(efa->h==0 && (efa->f & SELECT)) { + efa->v1->f1= efa->v2->f1= efa->v3->f1= SELECT; + if(efa->v4) efa->v4->f1= SELECT; + } + } + for(eve= em->verts.first; eve; eve= eve->next) if(eve->f1) tottrans++; + } + + /* proportional edit exception... */ + if((mode & 1) && tottrans) { + for(eve= em->verts.first; eve; eve= eve->next) { + if(eve->h==0) { + eve->f1 |= 2; + proptrans++; + } + } + if(proptrans>tottrans) tottrans= proptrans; + } + + /* and now make transverts */ + if(tottrans) { + tv=transvmain= MEM_callocN(tottrans*sizeof(TransVert), "maketransverts"); + + for(eve= em->verts.first; eve; eve= eve->next) { + if(eve->f1) { + VECCOPY(tv->oldloc, eve->co); + tv->loc= eve->co; + if(eve->no[0]!=0.0 || eve->no[1]!=0.0 ||eve->no[2]!=0.0) + tv->nor= eve->no; // note this is a hackish signal (ton) + tv->flag= eve->f1 & SELECT; + tv++; + } + } + } + } + else if (obedit->type==OB_ARMATURE){ + bArmature *arm= obedit->data; + int totmalloc= BLI_countlist(arm->edbo); + + tv=transvmain= MEM_callocN(totmalloc*sizeof(TransVert), "maketransverts armature"); + + for (ebo= arm->edbo->first; ebo; ebo=ebo->next){ + if(ebo->layer & arm->layer) { + short tipsel= (ebo->flag & BONE_TIPSEL); + short rootsel= (ebo->flag & BONE_ROOTSEL); + short rootok= (!(ebo->parent && (ebo->flag & BONE_CONNECTED) && ebo->parent->flag & BONE_TIPSEL)); + + if ((tipsel && rootsel) || (rootsel)) { + /* Don't add the tip (unless mode & 2, for getting all joints), + * otherwise we get zero-length bones as tips will snap to the same + * location as heads. + */ + if (rootok) { + VECCOPY (tv->oldloc, ebo->head); + tv->loc= ebo->head; + tv->nor= NULL; + tv->flag= 1; + tv++; + tottrans++; + } + + if ((mode & 2) && (tipsel)) { + VECCOPY (tv->oldloc, ebo->tail); + tv->loc= ebo->tail; + tv->nor= NULL; + tv->flag= 1; + tv++; + tottrans++; + } + } + else if (tipsel) { + VECCOPY (tv->oldloc, ebo->tail); + tv->loc= ebo->tail; + tv->nor= NULL; + tv->flag= 1; + tv++; + tottrans++; + } + } + } + } + else if (ELEM(obedit->type, OB_CURVE, OB_SURF)) { + Curve *cu= obedit->data; + int totmalloc= 0; + + for(nu= cu->editnurb->first; nu; nu= nu->next) { + if((nu->type & 7)==CU_BEZIER) + totmalloc += 3*nu->pntsu; + else + totmalloc += nu->pntsu*nu->pntsv; + } + tv=transvmain= MEM_callocN(totmalloc*sizeof(TransVert), "maketransverts curve"); + + nu= cu->editnurb->first; + while(nu) { + if((nu->type & 7)==CU_BEZIER) { + a= nu->pntsu; + bezt= nu->bezt; + while(a--) { + if(bezt->hide==0) { + if((mode & 1) || (bezt->f1 & SELECT)) { + VECCOPY(tv->oldloc, bezt->vec[0]); + tv->loc= bezt->vec[0]; + tv->flag= bezt->f1 & SELECT; + tv++; + tottrans++; + } + if((mode & 1) || (bezt->f2 & SELECT)) { + VECCOPY(tv->oldloc, bezt->vec[1]); + tv->loc= bezt->vec[1]; + tv->val= &(bezt->alfa); + tv->oldval= bezt->alfa; + tv->flag= bezt->f2 & SELECT; + tv++; + tottrans++; + } + if((mode & 1) || (bezt->f3 & SELECT)) { + VECCOPY(tv->oldloc, bezt->vec[2]); + tv->loc= bezt->vec[2]; + tv->flag= bezt->f3 & SELECT; + tv++; + tottrans++; + } + } + bezt++; + } + } + else { + a= nu->pntsu*nu->pntsv; + bp= nu->bp; + while(a--) { + if(bp->hide==0) { + if((mode & 1) || (bp->f1 & SELECT)) { + VECCOPY(tv->oldloc, bp->vec); + tv->loc= bp->vec; + tv->val= &(bp->alfa); + tv->oldval= bp->alfa; + tv->flag= bp->f1 & SELECT; + tv++; + tottrans++; + } + } + bp++; + } + } + nu= nu->next; + } + } + else if(obedit->type==OB_MBALL) { + MetaBall *mb= obedit->data; + int totmalloc= BLI_countlist(mb->editelems); + + tv=transvmain= MEM_callocN(totmalloc*sizeof(TransVert), "maketransverts mball"); + + ml= mb->editelems->first; + while(ml) { + if(ml->flag & SELECT) { + tv->loc= &ml->x; + VECCOPY(tv->oldloc, tv->loc); + tv->val= &(ml->rad); + tv->oldval= ml->rad; + tv->flag= 1; + tv++; + tottrans++; + } + ml= ml->next; + } + } + else if(obedit->type==OB_LATTICE) { + Lattice *lt= obedit->data; + + bp= lt->editlatt->def; + + a= lt->editlatt->pntsu*lt->editlatt->pntsv*lt->editlatt->pntsw; + + tv=transvmain= MEM_callocN(a*sizeof(TransVert), "maketransverts curve"); + + while(a--) { + if((mode & 1) || (bp->f1 & SELECT)) { + if(bp->hide==0) { + VECCOPY(tv->oldloc, bp->vec); + tv->loc= bp->vec; + tv->flag= bp->f1 & SELECT; + tv++; + tottrans++; + } + } + bp++; + } + } + + /* cent etc */ + tv= transvmain; + total= 0.0; + for(a=0; aflag & SELECT) { + centroid[0]+= tv->oldloc[0]; + centroid[1]+= tv->oldloc[1]; + centroid[2]+= tv->oldloc[2]; + total+= 1.0; + DO_MINMAX(tv->oldloc, min, max); + } + } + if(total!=0.0) { + centroid[0]/= total; + centroid[1]/= total; + centroid[2]/= total; + } + + center[0]= (min[0]+max[0])/2.0; + center[1]= (min[1]+max[1])/2.0; + center[2]= (min[2]+max[2])/2.0; + +} + +/* *********************** operators ******************** */ + +static int snap_sel_to_grid(bContext *C, wmOperator *op) +{ + extern float originmat[3][3]; /* XXX object.c */ + Object *obedit= CTX_data_edit_object(C); + Scene *scene= CTX_data_scene(C); + View3D *v3d= CTX_wm_view3d(C); + TransVert *tv; + Object *ob; + float gridf, imat[3][3], bmat[3][3], vec[3]; + int a; + + gridf= v3d->gridview; + + if(obedit) { + tottrans= 0; + + if ELEM6(obedit->type, OB_ARMATURE, OB_LATTICE, OB_MESH, OB_SURF, OB_CURVE, OB_MBALL) + make_trans_verts(obedit, bmat[0], bmat[1], 0); + if(tottrans==0) return OPERATOR_CANCELLED; + + Mat3CpyMat4(bmat, obedit->obmat); + Mat3Inv(imat, bmat); + + tv= transvmain; + for(a=0; aloc); + Mat3MulVecfl(bmat, vec); + VecAddf(vec, vec, obedit->obmat[3]); + vec[0]= v3d->gridview*floor(.5+ vec[0]/gridf); + vec[1]= v3d->gridview*floor(.5+ vec[1]/gridf); + vec[2]= v3d->gridview*floor(.5+ vec[2]/gridf); + VecSubf(vec, vec, obedit->obmat[3]); + + Mat3MulVecfl(imat, vec); + VECCOPY(tv->loc, vec); + } + + special_transvert_update(scene, obedit); + + MEM_freeN(transvmain); + transvmain= NULL; + + } + else { + + CTX_DATA_BEGIN(C, Base*, base, selected_editable_bases) { + ob= base->object; + + if(ob->flag & OB_POSEMODE) { + bPoseChannel *pchan; + bArmature *arm= ob->data; + + for (pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) { + if(pchan->bone->flag & BONE_SELECTED) { + if(pchan->bone->layer & arm->layer) { + if((pchan->bone->flag & BONE_CONNECTED)==0) { + float vecN[3], nLoc[3]; + + /* get nearest grid point to snap to */ + VECCOPY(nLoc, pchan->pose_mat[3]); + vec[0]= gridf * (float)(floor(.5+ nLoc[0]/gridf)); + vec[1]= gridf * (float)(floor(.5+ nLoc[1]/gridf)); + vec[2]= gridf * (float)(floor(.5+ nLoc[2]/gridf)); + + /* get bone-space location of grid point */ + armature_loc_pose_to_bone(pchan, vec, vecN); + + /* adjust location */ + VECCOPY(pchan->loc, vecN); + } + /* if the bone has a parent and is connected to the parent, + * don't do anything - will break chain unless we do auto-ik. + */ + } + } + } + ob->pose->flag |= (POSE_LOCKED|POSE_DO_UNLOCK); + + /* auto-keyframing */ +// XXX autokeyframe_pose_cb_func(ob, TFM_TRANSLATION, 0); + DAG_object_flush_update(scene, ob, OB_RECALC_DATA); + } + else { + ob->recalc |= OB_RECALC_OB; + + vec[0]= -ob->obmat[3][0]+v3d->gridview*floor(.5+ ob->obmat[3][0]/gridf); + vec[1]= -ob->obmat[3][1]+v3d->gridview*floor(.5+ ob->obmat[3][1]/gridf); + vec[2]= -ob->obmat[3][2]+v3d->gridview*floor(.5+ ob->obmat[3][2]/gridf); + + if(ob->parent) { + where_is_object(scene, ob); + + Mat3Inv(imat, originmat); + Mat3MulVecfl(imat, vec); + ob->loc[0]+= vec[0]; + ob->loc[1]+= vec[1]; + ob->loc[2]+= vec[2]; + } + else { + ob->loc[0]+= vec[0]; + ob->loc[1]+= vec[1]; + ob->loc[2]+= vec[2]; + } + + /* auto-keyframing */ +// XXX autokeyframe_ob_cb_func(ob, TFM_TRANSLATION); + } + } + CTX_DATA_END; + } + ED_anim_dag_flush_update(C); + WM_event_add_notifier(C, NC_OBJECT|ND_TRANSFORM, NULL); + + return OPERATOR_FINISHED; +} + +void VIEW3D_OT_snap_selected_to_grid(wmOperatorType *ot) +{ + + /* identifiers */ + ot->name= "Snap Selection to Grid"; + ot->idname= "VIEW3D_OT_snap_selected_to_grid"; + + /* api callbacks */ + ot->exec= snap_sel_to_grid; + ot->poll= ED_operator_view3d_active; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; +} + +/* *************************************************** */ + +static int snap_sel_to_curs(bContext *C, wmOperator *op) +{ + extern float originmat[3][3]; /* XXX object.c */ + Object *obedit= CTX_data_edit_object(C); + Scene *scene= CTX_data_scene(C); + View3D *v3d= CTX_wm_view3d(C); + TransVert *tv; + Object *ob; + float *curs, imat[3][3], bmat[3][3], vec[3]; + int a; + + curs= give_cursor(scene, v3d); + + if(obedit) { + tottrans= 0; + + if ELEM6(obedit->type, OB_ARMATURE, OB_LATTICE, OB_MESH, OB_SURF, OB_CURVE, OB_MBALL) + make_trans_verts(obedit, bmat[0], bmat[1], 0); + if(tottrans==0) return OPERATOR_CANCELLED; + + Mat3CpyMat4(bmat, obedit->obmat); + Mat3Inv(imat, bmat); + + tv= transvmain; + for(a=0; aobmat[3][0]; + vec[1]= curs[1]-obedit->obmat[3][1]; + vec[2]= curs[2]-obedit->obmat[3][2]; + + Mat3MulVecfl(imat, vec); + VECCOPY(tv->loc, vec); + } + + special_transvert_update(scene, obedit); + + MEM_freeN(transvmain); + transvmain= NULL; + + } + else { + CTX_DATA_BEGIN(C, Base*, base, selected_editable_bases) { + ob= base->object; + if(ob->flag & OB_POSEMODE) { + bPoseChannel *pchan; + bArmature *arm= ob->data; + float cursp[3]; + + Mat4Invert(ob->imat, ob->obmat); + VECCOPY(cursp, curs); + Mat4MulVecfl(ob->imat, cursp); + + for (pchan = ob->pose->chanbase.first; pchan; pchan=pchan->next) { + if(pchan->bone->flag & BONE_SELECTED) { + if(pchan->bone->layer & arm->layer) { + if((pchan->bone->flag & BONE_CONNECTED)==0) { + float curspn[3]; + + /* get location of cursor in bone-space */ + armature_loc_pose_to_bone(pchan, cursp, curspn); + + /* calculate new position */ + VECCOPY(pchan->loc, curspn); + } + /* if the bone has a parent and is connected to the parent, + * don't do anything - will break chain unless we do auto-ik. + */ + } + } + } + ob->pose->flag |= (POSE_LOCKED|POSE_DO_UNLOCK); + + /* auto-keyframing */ +// XXX autokeyframe_pose_cb_func(ob, TFM_TRANSLATION, 0); + DAG_object_flush_update(scene, ob, OB_RECALC_DATA); + } + else { + ob->recalc |= OB_RECALC_OB; + + vec[0]= -ob->obmat[3][0] + curs[0]; + vec[1]= -ob->obmat[3][1] + curs[1]; + vec[2]= -ob->obmat[3][2] + curs[2]; + + if(ob->parent) { + where_is_object(scene, ob); + + Mat3Inv(imat, originmat); + Mat3MulVecfl(imat, vec); + ob->loc[0]+= vec[0]; + ob->loc[1]+= vec[1]; + ob->loc[2]+= vec[2]; + } + else { + ob->loc[0]+= vec[0]; + ob->loc[1]+= vec[1]; + ob->loc[2]+= vec[2]; + } + /* auto-keyframing */ +// XXX autokeyframe_ob_cb_func(ob, TFM_TRANSLATION); + } + } + CTX_DATA_END; + } + ED_anim_dag_flush_update(C); + WM_event_add_notifier(C, NC_OBJECT|ND_TRANSFORM, NULL); + + return OPERATOR_FINISHED; +} + +void VIEW3D_OT_snap_selected_to_cursor(wmOperatorType *ot) +{ + + /* identifiers */ + ot->name= "Snap Selection to Cursor"; + ot->idname= "VIEW3D_OT_snap_selected_to_cursor"; + + /* api callbacks */ + ot->exec= snap_sel_to_curs; + ot->poll= ED_operator_view3d_active; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; +} + +/* *************************************************** */ + +static int snap_curs_to_grid(bContext *C, wmOperator *op) +{ + Scene *scene= CTX_data_scene(C); + View3D *v3d= CTX_wm_view3d(C); + float gridf, *curs; + + gridf= v3d->gridview; + curs= give_cursor(scene, v3d); + + curs[0]= v3d->gridview*floor(.5+curs[0]/gridf); + curs[1]= v3d->gridview*floor(.5+curs[1]/gridf); + curs[2]= v3d->gridview*floor(.5+curs[2]/gridf); + + WM_event_add_notifier(C, NC_SCENE|ND_TRANSFORM, scene); // hrm + + return OPERATOR_FINISHED; +} + +void VIEW3D_OT_snap_cursor_to_grid(wmOperatorType *ot) +{ + + /* identifiers */ + ot->name= "Snap Cursor to Grid"; + ot->idname= "VIEW3D_OT_snap_cursor_to_grid"; + + /* api callbacks */ + ot->exec= snap_curs_to_grid; + ot->poll= ED_operator_view3d_active; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; +} + +/* **************************************************** */ + +static int snap_curs_to_sel(bContext *C, wmOperator *op) +{ + Object *obedit= CTX_data_edit_object(C); + Scene *scene= CTX_data_scene(C); + View3D *v3d= CTX_wm_view3d(C); + TransVert *tv; + float *curs, bmat[3][3], vec[3], min[3], max[3], centroid[3]; + int count, a; + + curs= give_cursor(scene, v3d); + + count= 0; + INIT_MINMAX(min, max); + centroid[0]= centroid[1]= centroid[2]= 0.0; + + if(obedit) { + tottrans=0; + + if ELEM6(obedit->type, OB_ARMATURE, OB_LATTICE, OB_MESH, OB_SURF, OB_CURVE, OB_MBALL) + make_trans_verts(obedit, bmat[0], bmat[1], 2); + if(tottrans==0) return OPERATOR_CANCELLED; + + Mat3CpyMat4(bmat, obedit->obmat); + + tv= transvmain; + for(a=0; aloc); + Mat3MulVecfl(bmat, vec); + VecAddf(vec, vec, obedit->obmat[3]); + VecAddf(centroid, centroid, vec); + DO_MINMAX(vec, min, max); + } + + if(v3d->around==V3D_CENTROID) { + VecMulf(centroid, 1.0/(float)tottrans); + VECCOPY(curs, centroid); + } + else { + curs[0]= (min[0]+max[0])/2; + curs[1]= (min[1]+max[1])/2; + curs[2]= (min[2]+max[2])/2; + } + MEM_freeN(transvmain); + transvmain= NULL; + } + else { + Object *ob= OBACT; + + if(ob && (ob->flag & OB_POSEMODE)) { + bArmature *arm= ob->data; + bPoseChannel *pchan; + for (pchan = ob->pose->chanbase.first; pchan; pchan=pchan->next) { + if(arm->layer & pchan->bone->layer) { + if(pchan->bone->flag & BONE_SELECTED) { + VECCOPY(vec, pchan->pose_head); + Mat4MulVecfl(ob->obmat, vec); + VecAddf(centroid, centroid, vec); + DO_MINMAX(vec, min, max); + count++; + } + } + } + } + else { + CTX_DATA_BEGIN(C, Base*, base, selected_editable_bases) { + VECCOPY(vec, base->object->obmat[3]); + VecAddf(centroid, centroid, vec); + DO_MINMAX(vec, min, max); + count++; + } + CTX_DATA_END; + } + if(count) { + if(v3d->around==V3D_CENTROID) { + VecMulf(centroid, 1.0/(float)count); + VECCOPY(curs, centroid); + } + else { + curs[0]= (min[0]+max[0])/2; + curs[1]= (min[1]+max[1])/2; + curs[2]= (min[2]+max[2])/2; + } + } + } + WM_event_add_notifier(C, NC_SCENE|ND_TRANSFORM, scene); // hrm + + return OPERATOR_FINISHED; +} + +void VIEW3D_OT_snap_cursor_to_selected(wmOperatorType *ot) +{ + + /* identifiers */ + ot->name= "Snap Cursor to Selected"; + ot->idname= "VIEW3D_OT_snap_cursor_to_selected"; + + /* api callbacks */ + ot->exec= snap_curs_to_sel; + ot->poll= ED_operator_view3d_active; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; +} + +/* ********************************************** */ + +static int snap_curs_to_active(bContext *C, wmOperator *op) +{ + Object *obedit= CTX_data_edit_object(C); + Scene *scene= CTX_data_scene(C); + View3D *v3d= CTX_wm_view3d(C); + float *curs; + + curs = give_cursor(scene, v3d); + + if (obedit) { + if (obedit->type == OB_MESH) { + /* check active */ + Mesh *me= obedit->data; + EditSelection ese; + + if (EM_get_actSelection(me->edit_mesh, &ese)) { + EM_editselection_center(curs, &ese); + } + + Mat4MulVecfl(obedit->obmat, curs); + } + } + else { + if (BASACT) { + VECCOPY(curs, BASACT->object->obmat[3]); + } + } + + WM_event_add_notifier(C, NC_SCENE|ND_TRANSFORM, scene); + return OPERATOR_FINISHED; +} + +void VIEW3D_OT_snap_cursor_to_active(wmOperatorType *ot) +{ + + /* identifiers */ + ot->name= "Snap Cursor to Active"; + ot->idname= "VIEW3D_OT_snap_cursor_to_active"; + + /* api callbacks */ + ot->exec= snap_curs_to_active; + ot->poll= ED_operator_view3d_active; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; +} + +/* ************************************** */ + +static int snap_selected_to_center(bContext *C, wmOperator *op) +{ + extern float originmat[3][3]; /* XXX object.c */ + Object *obedit= CTX_data_edit_object(C); + Scene *scene= CTX_data_scene(C); + View3D *v3d= CTX_wm_view3d(C); + TransVert *tv; + Object *ob; + float snaploc[3], imat[3][3], bmat[3][3], vec[3], min[3], max[3], centroid[3]; + int count, a; + + /*calculate the snaplocation (centerpoint) */ + count= 0; + INIT_MINMAX(min, max); + centroid[0]= centroid[1]= centroid[2]= 0.0f; + snaploc[0]= snaploc[1]= snaploc[2]= 0.0f; + + if(obedit) { + tottrans= 0; + + if ELEM6(obedit->type, OB_ARMATURE, OB_LATTICE, OB_MESH, OB_SURF, OB_CURVE, OB_MBALL) + make_trans_verts(obedit, bmat[0], bmat[1], 0); + if(tottrans==0) return OPERATOR_CANCELLED; + + Mat3CpyMat4(bmat, obedit->obmat); + Mat3Inv(imat, bmat); + + tv= transvmain; + for(a=0; aloc); + Mat3MulVecfl(bmat, vec); + VecAddf(vec, vec, obedit->obmat[3]); + VecAddf(centroid, centroid, vec); + DO_MINMAX(vec, min, max); + } + + if(v3d->around==V3D_CENTROID) { + VecMulf(centroid, 1.0/(float)tottrans); + VECCOPY(snaploc, centroid); + } + else { + snaploc[0]= (min[0]+max[0])/2; + snaploc[1]= (min[1]+max[1])/2; + snaploc[2]= (min[2]+max[2])/2; + } + + MEM_freeN(transvmain); + transvmain= NULL; + } + else { + + CTX_DATA_BEGIN(C, Base*, base, selected_editable_bases) { + ob= base->object; + if(ob->flag & OB_POSEMODE) { + bPoseChannel *pchan; + bArmature *arm= ob->data; + + for (pchan = ob->pose->chanbase.first; pchan; pchan=pchan->next) { + if(pchan->bone->flag & BONE_SELECTED) { + if(pchan->bone->layer & arm->layer) { + VECCOPY(vec, pchan->pose_mat[3]); + VecAddf(centroid, centroid, vec); + DO_MINMAX(vec, min, max); + count++; + } + } + } + } + else { + /* not armature bones (i.e. objects) */ + VECCOPY(vec, base->object->obmat[3]); + VecAddf(centroid, centroid, vec); + DO_MINMAX(vec, min, max); + count++; + } + } + CTX_DATA_END; + + if(count) { + if(v3d->around==V3D_CENTROID) { + VecMulf(centroid, 1.0/(float)count); + VECCOPY(snaploc, centroid); + } + else { + snaploc[0]= (min[0]+max[0])/2; + snaploc[1]= (min[1]+max[1])/2; + snaploc[2]= (min[2]+max[2])/2; + } + } + } + + /* Snap the selection to the snaplocation (duh!) */ + if(obedit) { + tottrans= 0; + + if ELEM6(obedit->type, OB_ARMATURE, OB_LATTICE, OB_MESH, OB_SURF, OB_CURVE, OB_MBALL) + make_trans_verts(obedit, bmat[0], bmat[1], 0); + if(tottrans==0) return OPERATOR_CANCELLED; + + Mat3CpyMat4(bmat, obedit->obmat); + Mat3Inv(imat, bmat); + + tv= transvmain; + for(a=0; aobmat[3][0]; + vec[1]= snaploc[1]-obedit->obmat[3][1]; + vec[2]= snaploc[2]-obedit->obmat[3][2]; + + Mat3MulVecfl(imat, vec); + VECCOPY(tv->loc, vec); + } + + special_transvert_update(scene, obedit); + + MEM_freeN(transvmain); + transvmain= NULL; + + } + else { + + CTX_DATA_BEGIN(C, Base*, base, selected_editable_bases) { + ob= base->object; + if(ob->flag & OB_POSEMODE) { + bPoseChannel *pchan; + bArmature *arm= ob->data; + + for (pchan = ob->pose->chanbase.first; pchan; pchan=pchan->next) { + if(pchan->bone->flag & BONE_SELECTED) { + if(pchan->bone->layer & arm->layer) { + if((pchan->bone->flag & BONE_CONNECTED)==0) { + /* get location of cursor in bone-space */ + armature_loc_pose_to_bone(pchan, snaploc, vec); + + /* calculate new position */ + VECCOPY(pchan->loc, vec); + } + /* if the bone has a parent and is connected to the parent, + * don't do anything - will break chain unless we do auto-ik. + */ + } + } + } + + /* auto-keyframing */ + ob->pose->flag |= POSE_DO_UNLOCK; +// XXX autokeyframe_pose_cb_func(ob, TFM_TRANSLATION, 0); + DAG_object_flush_update(scene, ob, OB_RECALC_DATA); + } + else { + ob->recalc |= OB_RECALC_OB; + + vec[0]= -ob->obmat[3][0] + snaploc[0]; + vec[1]= -ob->obmat[3][1] + snaploc[1]; + vec[2]= -ob->obmat[3][2] + snaploc[2]; + + if(ob->parent) { + where_is_object(scene, ob); + + Mat3Inv(imat, originmat); + Mat3MulVecfl(imat, vec); + ob->loc[0]+= vec[0]; + ob->loc[1]+= vec[1]; + ob->loc[2]+= vec[2]; + } + else { + ob->loc[0]+= vec[0]; + ob->loc[1]+= vec[1]; + ob->loc[2]+= vec[2]; + } + /* auto-keyframing */ +// XXX autokeyframe_ob_cb_func(ob, TFM_TRANSLATION); + } + } + CTX_DATA_END; + } + + ED_anim_dag_flush_update(C); + WM_event_add_notifier(C, NC_OBJECT|ND_TRANSFORM, NULL); + + return OPERATOR_FINISHED; +} + +void VIEW3D_OT_snap_selected_to_center(wmOperatorType *ot) +{ + + /* identifiers */ + ot->name= "Snap Selection to Center"; + ot->idname= "VIEW3D_OT_snap_selected_to_center"; + + /* api callbacks */ + ot->exec= snap_selected_to_center; + ot->poll= ED_operator_view3d_active; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; +} + + +int minmax_verts(Object *obedit, float *min, float *max) +{ + TransVert *tv; + float centroid[3], vec[3], bmat[3][3]; + int a; + + tottrans=0; + if ELEM5(obedit->type, OB_ARMATURE, OB_LATTICE, OB_MESH, OB_SURF, OB_CURVE) + make_trans_verts(obedit, bmat[0], bmat[1], 2); + + if(tottrans==0) return 0; + + Mat3CpyMat4(bmat, obedit->obmat); + + tv= transvmain; + for(a=0; aloc); + Mat3MulVecfl(bmat, vec); + VecAddf(vec, vec, obedit->obmat[3]); + VecAddf(centroid, centroid, vec); + DO_MINMAX(vec, min, max); + } + + MEM_freeN(transvmain); + transvmain= NULL; + + return 1; +} + +/* ************************************************* */ + +static int snap_menu_invoke(bContext *C, wmOperator *unused, wmEvent *event) +{ + uiMenuItem *head= uiPupMenuBegin("Snap", 0); + + uiMenuItemO(head, 0, "VIEW3D_OT_snap_selected_to_grid"); + uiMenuItemO(head, 0, "VIEW3D_OT_snap_selected_to_cursor"); + uiMenuItemO(head, 0, "VIEW3D_OT_snap_selected_to_center"); + uiMenuSeparator(head); + uiMenuItemO(head, 0, "VIEW3D_OT_snap_cursor_to_selected"); + uiMenuItemO(head, 0, "VIEW3D_OT_snap_cursor_to_grid"); + uiMenuItemO(head, 0, "VIEW3D_OT_snap_cursor_to_active"); + + uiPupMenuEnd(C, head); + + /* this operator is only for a menu, not used further */ + return OPERATOR_CANCELLED; +} + +/* only used as menu */ +void VIEW3D_OT_snap_menu(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Snap Menu"; + ot->idname= "VIEW3D_OT_snap_menu"; + + /* api callbacks */ + ot->invoke= snap_menu_invoke; + + ot->poll= ED_operator_view3d_active; + + /* flags */ + ot->flag= 0; +} + + diff --git a/source/blender/python/BPY_menus.c b/source/blender/python/BPY_menus.c new file mode 100644 index 00000000000..b67f1e717da --- /dev/null +++ b/source/blender/python/BPY_menus.c @@ -0,0 +1,1118 @@ +/* + * $Id: BPY_menus.c 12932 2007-12-17 20:21:06Z theeth $ + * + * ***** BEGIN GPL/BL DUAL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. The Blender + * Foundation also sells licenses for use in proprietary software under + * the Blender License. See http://www.blender.org/BL/ for information + * about this. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * This is a new part of Blender. + * + * Contributor(s): Willian P. Germano, Michael Reimpell + * + * ***** END GPL/BL DUAL LICENSE BLOCK ***** +*/ + +/* + *This is the main file responsible for having bpython scripts accessible + * from Blender menus. To know more, please start with its header file. + */ + +#include "BPY_menus.h" + +#include +#ifndef WIN32 + #include +#else + #include "BLI_winstuff.h" +#endif +#include "BKE_global.h" +#include "BKE_utildefines.h" +#include "BLI_blenlib.h" +#include "MEM_guardedalloc.h" +#include "DNA_userdef_types.h" /* for U.pythondir */ +#include "api2_2x/EXPP_interface.h" /* for bpy_gethome() */ + +#define BPYMENU_DATAFILE "Bpymenus" +#define MAX_DIR_DEPTH 4 /* max depth for traversing scripts dirs */ +#define MAX_DIR_NUMBER 30 /* max number of dirs in scripts dirs trees */ + +static int DEBUG; +static int Dir_Depth; +static int Dirs_Number; + +/* BPyMenuTable holds all registered pymenus, as linked lists for each menu + * where they can appear (see PYMENUHOOKS enum in BPY_menus.h). +*/ +BPyMenu *BPyMenuTable[PYMENU_TOTAL]; + +static int bpymenu_group_atoi( char *str ) +{ + if( !strcmp( str, "Export" ) ) + return PYMENU_EXPORT; + else if( !strcmp( str, "Import" ) ) + return PYMENU_IMPORT; + else if( !strcmp( str, "Help" ) ) + return PYMENU_HELP; + else if( !strcmp( str, "HelpWebsites" ) ) + return PYMENU_HELPWEBSITES; + else if( !strcmp( str, "HelpSystem" ) ) + return PYMENU_HELPSYSTEM; + else if( !strcmp( str, "Render" ) ) + return PYMENU_RENDER; + else if( !strcmp( str, "System" ) ) + return PYMENU_SYSTEM; + else if( !strcmp( str, "Object" ) ) + return PYMENU_OBJECT; + else if( !strcmp( str, "Mesh" ) ) + return PYMENU_MESH; + else if( !strncmp( str, "Theme", 5 ) ) + return PYMENU_THEMES; + else if( !strcmp( str, "Add" ) ) + return PYMENU_ADD; + else if( !strcmp( str, "Wizards" ) ) + return PYMENU_WIZARDS; + else if( !strcmp( str, "Animation" ) ) + return PYMENU_ANIMATION; + else if( !strcmp( str, "Materials" ) ) + return PYMENU_MATERIALS; + else if( !strcmp( str, "UV" ) ) + return PYMENU_UV; + else if( !strcmp( str, "Image" ) ) + return PYMENU_IMAGE; + else if( !strcmp( str, "FaceSelect" ) ) + return PYMENU_FACESELECT; + else if( !strcmp( str, "WeightPaint" ) ) + return PYMENU_WEIGHTPAINT; + else if( !strcmp( str, "VertexPaint" ) ) + return PYMENU_VERTEXPAINT; + else if( !strcmp( str, "UVCalculation" ) ) + return PYMENU_UVCALCULATION; + else if( !strcmp( str, "Armature" ) ) + return PYMENU_ARMATURE; + else if( !strcmp( str, "ScriptTemplate" ) ) + return PYMENU_SCRIPTTEMPLATE; + else if( !strcmp( str, "MeshFaceKey" ) ) + return PYMENU_MESHFACEKEY; + else if( !strcmp( str, "AddMesh" ) ) + return PYMENU_ADDMESH; + /* "Misc" or an inexistent group name: use misc */ + else + return PYMENU_MISC; +} + +char *BPyMenu_group_itoa( short menugroup ) +{ + switch ( menugroup ) { + case PYMENU_EXPORT: + return "Export"; + break; + case PYMENU_IMPORT: + return "Import"; + break; + case PYMENU_ADD: + return "Add"; + break; + case PYMENU_HELP: + return "Help"; + break; + case PYMENU_HELPWEBSITES: + return "HelpWebsites"; + break; + case PYMENU_HELPSYSTEM: + return "HelpSystem"; + break; + case PYMENU_RENDER: + return "Render"; + break; + case PYMENU_SYSTEM: + return "System"; + break; + case PYMENU_OBJECT: + return "Object"; + break; + case PYMENU_MESH: + return "Mesh"; + break; + case PYMENU_THEMES: + return "Themes"; + break; + case PYMENU_WIZARDS: + return "Wizards"; + break; + case PYMENU_ANIMATION: + return "Animation"; + break; + case PYMENU_MATERIALS: + return "Materials"; + break; + case PYMENU_UV: + return "UV"; + break; + case PYMENU_IMAGE: + return "Image"; + break; + case PYMENU_FACESELECT: + return "FaceSelect"; + break; + case PYMENU_WEIGHTPAINT: + return "WeightPaint"; + break; + case PYMENU_VERTEXPAINT: + return "VertexPaint"; + break; + case PYMENU_UVCALCULATION: + return "UVCalculation"; + break; + case PYMENU_ARMATURE: + return "Armature"; + break; + case PYMENU_SCRIPTTEMPLATE: + return "ScriptTemplate"; + break; + case PYMENU_MESHFACEKEY: + return "MeshFaceKey"; + break; + case PYMENU_ADDMESH: + return "AddMesh"; + break; + case PYMENU_MISC: + return "Misc"; + break; + } + return NULL; +} + +/* BPyMenu_CreatePupmenuStr: + * build and return a meaninful string to be used by pupmenu(). The + * string is made of a bpymenu name as title and its submenus as possible + * choices for the user. +*/ +char *BPyMenu_CreatePupmenuStr( BPyMenu * pym, short menugroup ) +{ + BPySubMenu *pysm = pym->submenus; + char str[1024], str2[100]; + int i = 0, rlen; + + if( !pym || !pysm ) + return NULL; + + str[0] = '\0'; + + PyOS_snprintf( str2, sizeof( str2 ), "%s: %s%%t", + BPyMenu_group_itoa( menugroup ), pym->name ); + strcat( str, str2 ); + + while( pysm ) { + PyOS_snprintf( str2, sizeof( str2 ), "|%s%%x%d", pysm->name, + i ); + rlen = sizeof( str ) - strlen( str ); + strncat( str, str2, rlen ); + i++; + pysm = pysm->next; + } + + return BLI_strdup( str ); +} + +static void bpymenu_RemoveAllSubEntries( BPySubMenu * smenu ) +{ + BPySubMenu *tmp; + + while( smenu ) { + tmp = smenu->next; + if( smenu->name ) + MEM_freeN( smenu->name ); + if( smenu->arg ) + MEM_freeN( smenu->arg ); + MEM_freeN( smenu ); + smenu = tmp; + } + return; +} + +void BPyMenu_RemoveAllEntries( void ) +{ + BPyMenu *tmp, *pymenu; + int i; + + for( i = 0; i < PYMENU_TOTAL; i++ ) { + pymenu = BPyMenuTable[i]; + while( pymenu ) { + tmp = pymenu->next; + if( pymenu->name ) + MEM_freeN( pymenu->name ); + if( pymenu->filename ) + MEM_freeN( pymenu->filename ); + if( pymenu->tooltip ) + MEM_freeN( pymenu->tooltip ); + if( pymenu->submenus ) + bpymenu_RemoveAllSubEntries( pymenu-> + submenus ); + MEM_freeN( pymenu ); + pymenu = tmp; + } + BPyMenuTable[i] = NULL; + } + + Dirs_Number = 0; + Dir_Depth = 0; + + return; +} + +static BPyMenu *bpymenu_FindEntry( short group, char *name ) +{ + BPyMenu *pymenu; + + if( ( group < 0 ) || ( group >= PYMENU_TOTAL ) ) + return NULL; + + pymenu = BPyMenuTable[group]; + + while( pymenu ) { + if( !strcmp( pymenu->name, name ) ) + return pymenu; + pymenu = pymenu->next; + } + + return NULL; +} + +/* BPyMenu_GetEntry: + * given a group and a position, return the entry in that position from + * that group. +*/ +BPyMenu *BPyMenu_GetEntry( short group, short pos ) +{ + BPyMenu *pym = NULL; + + if( ( group < 0 ) || ( group >= PYMENU_TOTAL ) ) + return NULL; + + pym = BPyMenuTable[group]; + + while( pos-- ) { + if( pym ) + pym = pym->next; + else + break; + } + + return pym; /* found entry or NULL */ +} + +static void bpymenu_set_tooltip( BPyMenu * pymenu, char *tip ) +{ + if( !pymenu ) + return; + + if( pymenu->tooltip ) + MEM_freeN( pymenu->tooltip ); + pymenu->tooltip = BLI_strdup( tip ); + + return; +} + +/* bpymenu_AddEntry: + * try to find an existing pymenu entry with the given type and name; + * if found, update it with new info, otherwise create a new one and fill it. + */ +static BPyMenu *bpymenu_AddEntry( short group, short version, char *name, + char *fname, int is_userdir, char *tooltip ) +{ + BPyMenu *menu, *next = NULL, **iter; + int nameclash = 0; + + if( ( group < 0 ) || ( group >= PYMENU_TOTAL ) ) + return NULL; + if( !name || !fname ) + return NULL; + + menu = bpymenu_FindEntry( group, name ); /* already exists? */ + + /* if a menu with this name already exists in the same group: + * - if one script is in the default dir and the other in U.pythondir, + * accept and let the new one override the other. + * - otherwise, report the error and return NULL. */ + if( menu ) { + if( menu->dir < is_userdir ) { /* new one is in U.pythondir */ + nameclash = 1; + if( menu->name ) + MEM_freeN( menu->name ); + if( menu->filename ) + MEM_freeN( menu->filename ); + if( menu->tooltip ) + MEM_freeN( menu->tooltip ); + if( menu->submenus ) + bpymenu_RemoveAllSubEntries( menu->submenus ); + next = menu->next; + } else { /* they are in the same dir */ + if (DEBUG) { + fprintf(stderr, "\n\ +Warning: script %s's menu name is already in use.\n\ +Edit the script and change its \n\ +Name: '%s'\n\ +field, please.\n\ +Note: if you really want to have two scripts for the same menu with\n\ +the same name, keep one in the default dir and the other in\n\ +the user defined dir (only the later will be registered).\n", fname, name); + } + return NULL; + } + } else + menu = MEM_mallocN( sizeof( BPyMenu ), "pymenu" ); + + if( !menu ) + return NULL; + + menu->name = BLI_strdup( name ); + menu->version = version; + menu->filename = BLI_strdup( fname ); + menu->tooltip = NULL; + if( tooltip ) + menu->tooltip = BLI_strdup( tooltip ); + menu->dir = is_userdir; + menu->submenus = NULL; + menu->next = next; /* non-NULL if menu already existed */ + + if( nameclash ) + return menu; /* no need to place it, it's already at the list */ + else { /* insert the new entry in its correct position at the table */ + BPyMenu *prev = NULL; + char *s = NULL; + + iter = &BPyMenuTable[group]; + while( *iter ) { + s = ( *iter )->name; + if( s ) + if( strcmp( menu->name, s ) < 0 ) + break; /* sort by names */ + prev = *iter; + iter = &( ( *iter )->next ); + } + + if( *iter ) { /* prepend */ + menu->next = *iter; + if( prev ) + prev->next = menu; + else + BPyMenuTable[group] = menu; /* is first entry */ + } else + *iter = menu; /* append */ + } + + return menu; +} + +/* bpymenu_AddSubEntry: + * add a submenu to an existing python menu. + */ +static int bpymenu_AddSubEntry( BPyMenu * mentry, char *name, char *arg ) +{ + BPySubMenu *smenu, **iter; + + smenu = MEM_mallocN( sizeof( BPySubMenu ), "pysubmenu" ); + if( !smenu ) + return -1; + + smenu->name = BLI_strdup( name ); + smenu->arg = BLI_strdup( arg ); + smenu->next = NULL; + + if( !smenu->name || !smenu->arg ) + return -1; + + iter = &( mentry->submenus ); + while( *iter ) + iter = &( ( *iter )->next ); + + *iter = smenu; + + return 0; +} + +/* bpymenu_CreateFromFile: + * parse the bpymenus data file where Python menu data is stored; + * based on this data, create and fill the pymenu structs. + */ +static int bpymenu_CreateFromFile( void ) +{ + FILE *fp; + char line[255], w1[255], w2[255], tooltip[255], *tip; + char *homedir = NULL; + int parsing, version, is_userdir; + short group; + BPyMenu *pymenu = NULL; + + /* init global bpymenu table (it is a list of pointers to struct BPyMenus + * for each available cathegory: import, export, etc.) */ + for( group = 0; group < PYMENU_TOTAL; group++ ) + BPyMenuTable[group] = NULL; + + /* let's try to open the file with bpymenu data */ + homedir = bpy_gethome(0); + if (!homedir) { + if( DEBUG ) + fprintf(stderr, + "BPyMenus error: couldn't open config file Bpymenus: no home dir.\n"); + return -1; + } + + BLI_make_file_string( "/", line, homedir, BPYMENU_DATAFILE ); + + fp = fopen( line, "rb" ); + + if( !fp ) { + if( DEBUG ) + fprintf(stderr, "BPyMenus error: couldn't open config file %s.\n", line ); + return -1; + } + + fgets( line, 255, fp ); /* header */ + + /* check if the U.pythondir we saved at the file is different from the + * current one. If so, return to force updating from dirs */ + w1[0] = '\0'; + fscanf( fp, "# User defined scripts dir: %[^\n]\n", w1 ); + if( w1 ) { + char upythondir[FILE_MAXDIR]; + + BLI_strncpy(upythondir, U.pythondir, FILE_MAXDIR); + BLI_convertstringcode(upythondir, G.sce, 0); + if( strcmp( w1, upythondir ) != 0 ) + return -1; + w1[0] = '\0'; + } + + while( fgets( line, 255, fp ) ) { /* parsing file lines */ + + switch ( line[0] ) { /* check first char */ + case '#': /* comment */ + continue; + break; + case '\n': + continue; + break; + default: + parsing = sscanf( line, "%s {\n", w1 ); /* menu group */ + break; + } + + if( parsing == 1 ) { /* got menu group string */ + group = (short)bpymenu_group_atoi( w1 ); + if( group < 0 && DEBUG ) { /* invalid type */ + fprintf(stderr, + "BPyMenus error parsing config file: wrong group: %s,\n\ +will use 'Misc'.\n", w1 ); + } + } else + continue; + + for(;;) { + tip = NULL; /* optional tooltip */ + fgets( line, 255, fp ); + if( line[0] == '}' ) + break; + else if( line[0] == '\n' ) + continue; + else if( line[0] == '\'' ) { /* menu entry */ + parsing = + sscanf( line, + "'%[^']' %d %s %d '%[^']'\n", + w1, &version, w2, &is_userdir, + tooltip ); + + if( parsing <= 0 ) { /* invalid line, get rid of it */ + fgets( line, 255, fp ); + } else if( parsing == 5 ) + tip = tooltip; /* has tooltip */ + + pymenu = bpymenu_AddEntry( group, + ( short ) version, + w1, w2, is_userdir, + tip ); + if( !pymenu ) { + puts( "BPyMenus error: couldn't create bpymenu entry.\n" ); + fclose( fp ); + return -1; + } + } else if( line[0] == '|' && line[1] == '_' ) { /* menu sub-entry */ + if( !pymenu ) + continue; /* no menu yet, skip this line */ + sscanf( line, "|_%[^:]: %s\n", w1, w2 ); + bpymenu_AddSubEntry( pymenu, w1, w2 ); + } + } + } + + fclose( fp ); + return 0; +} + +/* bpymenu_WriteDataFile: + * writes the registered scripts info to the user's home dir, for faster + * access when the scripts dir hasn't changed. +*/ +static void bpymenu_WriteDataFile( void ) +{ + BPyMenu *pymenu; + BPySubMenu *smenu; + FILE *fp; + char fname[FILE_MAXDIR], *homedir; + int i; + + homedir = bpy_gethome(0); + + if (!homedir) { + if( DEBUG ) + fprintf(stderr, + "BPyMenus error: couldn't write Bpymenus file: no home dir.\n\n"); + return; + } + + BLI_make_file_string( "/", fname, homedir, BPYMENU_DATAFILE ); + + fp = fopen( fname, "w" ); + if( !fp ) { + if( DEBUG ) + fprintf(stderr, "BPyMenus error: couldn't write %s file.\n\n", + fname ); + return; + } + + fprintf( fp, + "# Blender: registered menu entries for bpython scripts\n" ); + + if (U.pythondir[0] != '\0' && + strcmp(U.pythondir, "/") != 0 && strcmp(U.pythondir, "//") != 0) + { + char upythondir[FILE_MAXDIR]; + + BLI_strncpy(upythondir, U.pythondir, FILE_MAXDIR); + BLI_convertstringcode(upythondir, G.sce, 0); + fprintf( fp, "# User defined scripts dir: %s\n", upythondir ); + } + + for( i = 0; i < PYMENU_TOTAL; i++ ) { + pymenu = BPyMenuTable[i]; + if( !pymenu ) + continue; + fprintf( fp, "\n%s {\n", BPyMenu_group_itoa( (short)i ) ); + while( pymenu ) { + fprintf( fp, "'%s' %d %s %d", pymenu->name, + pymenu->version, pymenu->filename, + pymenu->dir ); + if( pymenu->tooltip ) + fprintf( fp, " '%s'\n", pymenu->tooltip ); + else + fprintf( fp, "\n" ); + smenu = pymenu->submenus; + while( smenu ) { + fprintf( fp, "|_%s: %s\n", smenu->name, + smenu->arg ); + smenu = smenu->next; + } + pymenu = pymenu->next; + } + fprintf( fp, "}\n" ); + } + + fclose( fp ); + return; +} + +/* BPyMenu_PrintAllEntries: + * useful for debugging. + */ +void BPyMenu_PrintAllEntries( void ) +{ + BPyMenu *pymenu; + BPySubMenu *smenu; + int i; + + printf( "# Blender: registered menu entries for bpython scripts\n" ); + + for( i = 0; i < PYMENU_TOTAL; i++ ) { + pymenu = BPyMenuTable[i]; + printf( "\n%s {\n", BPyMenu_group_itoa( (short)i ) ); + while( pymenu ) { + printf( "'%s' %d %s %d", pymenu->name, pymenu->version, + pymenu->filename, pymenu->dir ); + if( pymenu->tooltip ) + printf( " '%s'\n", pymenu->tooltip ); + else + printf( "\n" ); + smenu = pymenu->submenus; + while( smenu ) { + printf( "|_%s: %s\n", smenu->name, + smenu->arg ); + smenu = smenu->next; + } + pymenu = pymenu->next; + } + printf( "}\n" ); + } +} + +/* bpymenu_ParseFile: + * recursively scans folders looking for scripts to register. + * + * This function scans the scripts directory looking for .py files with the + * right header and menu info, using that to fill the bpymenu structs. + * is_userdir defines if the script is in the default scripts dir or the + * user defined one (U.pythondir: is_userdir == 1). + * Speed is important. + * + * The first line of the script must be '#!BPY'. + * The header registration lines must appear between the first pair of + * '\"\"\"' and follow this order (the single-quotes are part of + * the format): + * + * # \"\"\"
+ * # Name: 'script name for the menu' + * # Blender: short int (minimal Blender version) + * # Group: 'group name' (defines menu) + * # Submenu: 'submenu name' related_1word_arg + * # Tooltip: 'tooltip for the menu' + * # \"\"\" + * + * Notes: + * + * - Commenting out header lines with "#" is optional, but recommended. + * - There may be more than one submenu line, or none: + * submenus and the tooltip are optional; + * - The Blender version is the same number reported by + * Blender.Get('version') in BPython or G.version in C; + * - Line length must be less than 99. + */ +static int bpymenu_ParseFile(FILE *file, char *fname, int is_userdir) +{ + char line[100]; + char head[100]; + char middle[100]; + char tail[100]; + int matches; + int parser_state; + + char script_name[100]; + int script_version = 1; + int script_group; + + BPyMenu *scriptMenu = NULL; + + if (file != NULL) { + parser_state = 1; /* state of parser, 0 to terminate */ + + while ((parser_state != 0) && (fgets(line, 100, file) != NULL)) { + + switch (parser_state) { + + case 1: /* !BPY */ + if (strncmp(line, "#!BPY", 5) == 0) { + parser_state++; + } else { + parser_state = 0; + } + break; + + case 2: /* \"\"\" */ + if ((strstr(line, "\"\"\""))) { + parser_state++; + } + break; + + case 3: /* Name: 'script name for the menu' */ + matches = sscanf(line, "%[^']'%[^']'%c", head, script_name, tail); + if ((matches == 3) && (strstr(head, "Name:") != NULL)) { + parser_state++; + } else { + if (DEBUG) + fprintf(stderr, "BPyMenus error: Wrong 'Name' line: %s\n", fname); + parser_state = 0; + } + break; + + case 4: /* Blender: */ + matches = sscanf(line, "%[^1234567890]%i%c", head, &script_version, + tail); + if (matches == 3) { + parser_state++; + } else { + if (DEBUG) + fprintf(stderr,"BPyMenus error: Wrong 'Blender' line: %s\n",fname); + parser_state = 0; + } + break; + + case 5: /* Group: 'group name' */ + matches = sscanf(line, "%[^']'%[^']'%c", head, middle, tail); + if ((matches == 3) && (strstr(head, "Group:") != NULL)) { + script_group = bpymenu_group_atoi(middle); + if (script_group < 0) { + if (DEBUG) + fprintf(stderr, "BPyMenus error: Unknown group \"%s\": %s\n", + middle, fname); + parser_state = 0; + } + + else { /* register script */ + scriptMenu = bpymenu_AddEntry((short)script_group, + (short int)script_version, script_name, fname, is_userdir,NULL); + if (scriptMenu == NULL) { + if (DEBUG) + fprintf(stderr, + "BPyMenus error: Couldn't create entry for: %s\n", fname); + parser_state = 0; + } else { + parser_state++; + } + } + + } else { + if (DEBUG) + fprintf(stderr, "BPyMenus error: Wrong 'Group' line: %s\n",fname); + parser_state = 0; + } + break; + + case 6: /* optional elements */ + /* Submenu: 'submenu name' related_1word_arg */ + matches = sscanf(line, "%[^']'%[^']'%s\n", head, middle, tail); + if ((matches == 3) && (strstr(head, "Submenu:") != NULL)) { + bpymenu_AddSubEntry(scriptMenu, middle, tail); + } else { + /* Tooltip: 'tooltip for the menu */ + matches = sscanf(line, "%[^']'%[^']'%c", head, middle, tail); + if ((matches == 3) && ((strstr(head, "Tooltip:") != NULL) || + (strstr(head, "Tip:") != NULL))) { + bpymenu_set_tooltip(scriptMenu, middle); + } + parser_state = 0; + } + break; + + default: + parser_state = 0; + break; + } + } + } + + else { /* shouldn't happen, it's checked in bpymenus_ParseDir */ + if (DEBUG) + fprintf(stderr, "BPyMenus error: Couldn't open %s.\n", fname); + return -1; + } + + return 0; +} + +/* bpymenu_ParseDir: + * recursively scans folders looking for scripts to register. + * + * This function scans the scripts directory looking for .py files with the + * right header and menu info. + * - is_userdir defines if the script is in the default scripts dir or the + * user defined one (U.pythondir: is_userdir == 1); + * - parentdir is the parent dir name to store as part of the script filename, + * if we're down a subdir. + * Speed is important. + */ +static int bpymenu_ParseDir(char *dirname, char *parentdir, int is_userdir ) +{ + DIR *dir; + FILE *file = NULL; + struct dirent *de; + struct stat status; + char *file_extension; + char path[FILE_MAX]; + char subdir[FILE_MAX]; + char *s = NULL; + + dir = opendir(dirname); + + if (dir != NULL) { + while ((de = readdir(dir)) != NULL) { + + /* skip files and dirs starting with '.' or 'bpy' */ + if ((de->d_name[0] == '.') || !strncmp(de->d_name, "bpy", 3)) { + continue; + } + + BLI_make_file_string("/", path, dirname, de->d_name); + + if (stat(path, &status) != 0) { + if (DEBUG) + fprintf(stderr, "stat %s failed: %s\n", path, strerror(errno)); + } + + if (S_ISREG(status.st_mode)) { /* is file */ + + file_extension = strstr(de->d_name, ".py"); + + if (file_extension && *(file_extension + 3) == '\0') { + file = fopen(path, "rb"); + + if (file) { + s = de->d_name; + if (parentdir) { + /* Join parentdir and de->d_name */ + BLI_join_dirfile(subdir, parentdir, de->d_name); + + s = subdir; + } + bpymenu_ParseFile(file, s, is_userdir); + fclose(file); + } + + else { + if (DEBUG) + fprintf(stderr, "BPyMenus error: Couldn't open %s.\n", path); + } + } + } + + else if (S_ISDIR(status.st_mode)) { /* is subdir */ + Dirs_Number++; + Dir_Depth++; + if (Dirs_Number > MAX_DIR_NUMBER) { + if (DEBUG) { + fprintf(stderr, "BPyMenus error: too many subdirs.\n"); + } + closedir(dir); + return -1; + } + else if (Dir_Depth > MAX_DIR_DEPTH) { + if (DEBUG) + fprintf(stderr, + "BPyMenus error: max depth reached traversing dir tree.\n"); + closedir(dir); + return -1; + } + s = de->d_name; + if (parentdir) { + /* Join parentdir and de->d_name */ + BLI_join_dirfile(subdir, parentdir, de->d_name); + s = subdir; + } + if (bpymenu_ParseDir(path, s, is_userdir) == -1) { + closedir(dir); + return -1; + } + Dir_Depth--; + } + + } + closedir(dir); + } + + else { /* open directory stream failed */ + if (DEBUG) + fprintf(stderr, "opendir %s failed: %s\n", dirname, strerror(errno)); + return -1; + } + + return 0; +} + +static int bpymenu_GetStatMTime( char *name, int is_file, time_t * mtime ) +{ + struct stat st; + int result; + + result = stat( name, &st ); + + if( result == -1 ) + return -1; + + if( is_file ) { + if( !S_ISREG( st.st_mode ) ) + return -2; + } else if( !S_ISDIR( st.st_mode ) ) + return -2; + + *mtime = st.st_mtime; + + return 0; +} + +/* BPyMenu_Init: + * import the bpython menus data to Blender, either from: + * - the BPYMENU_DATAFILE file (?/.blender/Bpymenus) or + * - the scripts dir(s), case newer than the datafile (then update the file). + * then fill the bpymenu table with this data. + * if param usedir != 0, then the data is recreated from the dir(s) anyway. +*/ +int BPyMenu_Init( int usedir ) +{ + char fname[FILE_MAXDIR]; + char dirname[FILE_MAXDIR]; + char upythondir[FILE_MAXDIR]; + char *upydir = U.pythondir, *sdir = NULL; + time_t time_dir1 = 0, time_dir2 = 0, time_file = 0; + int stat_dir1 = 0, stat_dir2 = 0, stat_file = 0; + int i; + + DEBUG = G.f & G_DEBUG; /* is Blender in debug mode (started with -d) ? */ + + /* init global bpymenu table (it is a list of pointers to struct BPyMenus + * for each available group: import, export, etc.) */ + for( i = 0; i < PYMENU_TOTAL; i++ ) + BPyMenuTable[i] = NULL; + + if( DEBUG ) + fprintf(stdout, "\nRegistering scripts in Blender menus ...\n\n" ); + + if( U.pythondir[0] == '\0') { + upydir = NULL; + } + else if (strcmp(U.pythondir, "/") == 0 || strcmp(U.pythondir, "//") == 0) { + /* these are not accepted to prevent possible slight slowdowns on startup; + * they should not be used as user defined scripts dir, anyway, also from + * speed considerations, since they'd not be dedicated scripts dirs */ + if (DEBUG) fprintf(stderr, + "BPyMenus: invalid user defined Python scripts dir: \"/\" or \"//\".\n"); + upydir = NULL; + } + else { + BLI_strncpy(upythondir, upydir, FILE_MAXDIR); + BLI_convertstringcode(upythondir, G.sce, 0); + } + + sdir = bpy_gethome(1); + + if (sdir) { + BLI_strncpy(dirname, sdir, FILE_MAXDIR); + stat_dir1 = bpymenu_GetStatMTime( dirname, 0, &time_dir1 ); + + if( stat_dir1 < 0 ) { + time_dir1 = 0; + if( DEBUG ) { + fprintf(stderr, + "\nDefault scripts dir: %s:\n%s\n", dirname, strerror(errno)); + if( upydir ) + fprintf(stdout, + "Getting scripts menu data from user defined dir: %s.\n", + upythondir ); + } + } + } + else stat_dir1 = -1; + + if( upydir ) { + stat_dir2 = bpymenu_GetStatMTime( upythondir, 0, &time_dir2 ); + + if( stat_dir2 < 0 ) { + time_dir2 = 0; + upydir = NULL; + if( DEBUG ) + fprintf(stderr, "\nUser defined scripts dir: %s:\n%s.\n", + upythondir, strerror( errno ) ); + if( stat_dir1 < 0 ) { + if( DEBUG ) + fprintf(stderr, "\ +To have scripts in menus, please add them to the default scripts dir:\n\ +%s\n\ +and / or go to 'Info window -> File Paths tab' and set a valid path for\n\ +the user defined Python scripts dir.\n", dirname ); + return -1; + } + } + } + else stat_dir2 = -1; + + if( ( stat_dir1 < 0 ) && ( stat_dir2 < 0 ) ) { + if( DEBUG ) { + fprintf(stderr, "\nCannot register scripts in menus, no scripts dir" + " available.\nExpected default dir at: %s \n", dirname ); + } + return -1; + } + + if (usedir) stat_file = -1; + else { /* if we're not forced to use the dir */ + char *homedir = bpy_gethome(0); + + if (homedir) { + BLI_make_file_string( "/", fname, homedir, BPYMENU_DATAFILE ); + stat_file = bpymenu_GetStatMTime( fname, 1, &time_file ); + if( stat_file < 0 ) + time_file = 0; + + /* comparing dates */ + + if((stat_file == 0) + && (time_file > time_dir1) && (time_file > time_dir2)) + { /* file is newer */ + stat_file = bpymenu_CreateFromFile( ); /* -1 if an error occurred */ + if( !stat_file && DEBUG ) + fprintf(stdout, + "Getting menu data for scripts from file:\n%s\n\n", fname ); + } + else stat_file = -1; + } + else stat_file = -1; /* -1 to use dirs: didn't use file or it was corrupted */ + } + + if( stat_file == -1 ) { /* use dirs */ + if( DEBUG ) { + fprintf(stdout, + "Getting menu data for scripts from dir(s):\ndefault: %s\n", dirname ); + if( upydir ) + fprintf(stdout, "user defined: %s\n", upythondir ); + fprintf(stdout, "\n"); + } + if( stat_dir1 == 0 ) { + i = bpymenu_ParseDir( dirname, NULL, 0 ); + if (i == -1 && DEBUG) + fprintf(stderr, "Default scripts dir does not seem valid.\n\n"); + } + if( stat_dir2 == 0 ) { + BLI_strncpy(dirname, U.pythondir, FILE_MAXDIR); + BLI_convertstringcode(dirname, G.sce, 0); + i = bpymenu_ParseDir( dirname, NULL, 1 ); + if (i == -1 && DEBUG) + fprintf(stderr, "User defined scripts dir does not seem valid.\n\n"); + } + + /* check if we got any data */ + for( i = 0; i < PYMENU_TOTAL; i++ ) + if( BPyMenuTable[i] ) + break; + + /* if we got, recreate the file */ + if( i < PYMENU_TOTAL ) + bpymenu_WriteDataFile( ); + else if( DEBUG ) { + fprintf(stderr, "\n\ +Warning: Registering scripts in menus -- no info found.\n\ +Either your scripts dirs have no .py scripts or the scripts\n\ +don't have a header with registration data.\n\ +Default scripts dir is:\n\ +%s\n", dirname ); + if( upydir ) + fprintf(stderr, "User defined scripts dir is: %s\n", + upythondir ); + } + } + + return 0; +} diff --git a/source/blender/python/BPY_menus.h b/source/blender/python/BPY_menus.h new file mode 100644 index 00000000000..6cdea608b10 --- /dev/null +++ b/source/blender/python/BPY_menus.h @@ -0,0 +1,128 @@ +/* + * $Id: BPY_menus.h 12931 2007-12-17 18:20:48Z theeth $ + * + * ***** BEGIN GPL/BL DUAL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. The Blender + * Foundation also sells licenses for use in proprietary software under + * the Blender License. See http://www.blender.org/BL/ for information + * about this. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * This is a new part of Blender. + * + * Contributor(s): Willian P. Germano, Matt Ebb + * + * ***** END GPL/BL DUAL LICENSE BLOCK ***** +*/ + +#ifndef BPY_MENUS_H +#define BPY_MENUS_H + +/* This header exposes BPyMenu related public declarations. The implementation + * adds 'dynamic' menus to Blender, letting scripts register themselves in any + * of a few pre-defined (trivial to upgrade) places in menus. These places or + * slots are called groups here (Import, Export, etc). This is how it works: + * - scripts at dirs user pref U.pythondir and .blender/scripts/ are scanned + * for registration info. + * - this data is also saved to a Bpymenus file at the user's .blender/ dir and + * only re-created when the scripts folder gets modified. + * - on start-up Blender uses this info to fill a table, which is used to + * create the menu entries when they are needed (see header_info.c or + * header_script.c, under source/blender/src/, for examples). +*/ + +/* These two structs hold py menu/submenu info. + * BPyMenu holds a script's name (as should appear in the menu) and filename, + * plus an optional list of submenus. Each submenu is related to a string + * (arg) that the script can get from the __script__ pydict, to know which + * submenu was chosen. */ + +typedef struct BPySubMenu { + char *name; + char *arg; + struct BPySubMenu *next; +} BPySubMenu; + +typedef struct BPyMenu { + char *name; + char *filename; + char *tooltip; + short version; /* Blender version */ + int dir; /* 0: default, 1: U.pythondir */ + struct BPySubMenu *submenus; + struct BPyMenu *next; +} BPyMenu; + +/* Scripts can be added to only a few pre-defined places in menus, like + * File->Import, File->Export, etc. (for speed and better control). + * To make a new menu 'slot' available for scripts: + * - add an entry to the enum below, before PYMENU_TOTAL, of course; + * - update the bpymenu_group_atoi() and BPyMenu_group_itoa() functions in + * BPY_menus.c; + * - add the necessary code to the header_***.c file in + * source/blender/src/, like done in header_info.c for import/export; +*/ +typedef enum { + PYMENU_ADD,/* creates new objects */ + PYMENU_ANIMATION, + PYMENU_EXPORT, + PYMENU_IMPORT, + PYMENU_MATERIALS, + PYMENU_MESH, + PYMENU_MISC, + PYMENU_OBJECT, + PYMENU_RENDER,/* exporters to external renderers */ + PYMENU_SYSTEM, + PYMENU_THEMES, + PYMENU_UV,/* UV editing tools, to go in UV/Image editor space, 'UV' menu */ + PYMENU_IMAGE,/* Image editing tools, to go in UV/Image editor space, 'Image' menu */ + PYMENU_WIZARDS,/* complex 'app' scripts */ + + /* entries put after Wizards don't appear at the Scripts win->Scripts menu; + * see define right below */ + + PYMENU_FACESELECT, + PYMENU_WEIGHTPAINT, + PYMENU_VERTEXPAINT, + PYMENU_UVCALCULATION, + PYMENU_ARMATURE, + PYMENU_SCRIPTTEMPLATE, + PYMENU_HELP,/*Main Help menu items - prob best to leave for 'official' ones*/ + PYMENU_HELPSYSTEM,/* Resources, troubleshooting, system tools */ + PYMENU_HELPWEBSITES,/* Help -> Websites submenu */ + PYMENU_MESHFACEKEY, /* face key in mesh editmode */ + PYMENU_ADDMESH, /* adds mesh */ + PYMENU_TOTAL +} PYMENUHOOKS; + +#define PYMENU_SCRIPTS_MENU_TOTAL (PYMENU_WIZARDS + 1) + +/* BPyMenuTable holds all registered pymenus, as linked lists for each menu + * where they can appear (see PYMENUHOOKS enum above). +*/ +extern BPyMenu *BPyMenuTable[]; /* defined in BPY_menus.c */ + +/* public functions: */ +int BPyMenu_Init( int usedir ); +void BPyMenu_RemoveAllEntries( void ); +void BPyMenu_PrintAllEntries( void ); +char *BPyMenu_CreatePupmenuStr( BPyMenu * pym, short group ); +char *BPyMenu_group_itoa( short group ); +struct BPyMenu *BPyMenu_GetEntry( short group, short pos ); + +#endif /* BPY_MENUS_H */ diff --git a/source/blender/python/intern/bpy_ui.c b/source/blender/python/intern/bpy_ui.c new file mode 100644 index 00000000000..f622c130d6f --- /dev/null +++ b/source/blender/python/intern/bpy_ui.c @@ -0,0 +1,255 @@ +/** + * $Id$ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Contributor(s): Campbell Barton + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "bpy_ui.h" +#include "bpy_compat.h" + +#include "BLI_dynstr.h" + +#include "MEM_guardedalloc.h" +#include "BKE_global.h" /* evil G.* */ +#include "BKE_context.h" + +#include "DNA_screen_types.h" +#include "UI_interface.h" +#include "WM_api.h" + +static PyObject *Method_pupMenuBegin( PyObject * self, PyObject * args ) +{ + char *title; int icon; + + if( !PyArg_ParseTuple( args, "si:pupMenuBegin", &title, &icon)) + return NULL; + + return PyCObject_FromVoidPtr( uiPupMenuBegin(title, icon), NULL ); +} + +static PyObject *Method_pupMenuEnd( PyObject * self, PyObject * args ) +{ + PyObject *py_context, *py_head; + + if( !PyArg_ParseTuple( args, "O!O!:pupMenuEnd", &PyCObject_Type, &py_context, &PyCObject_Type, &py_head)) + return NULL; + + uiPupMenuEnd(PyCObject_AsVoidPtr(py_context), PyCObject_AsVoidPtr(py_head)); + + Py_RETURN_NONE; +} + +static PyObject *Method_menuItemO( PyObject * self, PyObject * args ) +{ + PyObject *py_head; + char *opname; + int icon; + + if( !PyArg_ParseTuple( args, "O!is:menuItemO", &PyCObject_Type, &py_head, &icon, &opname)) + return NULL; + + uiMenuItemO(PyCObject_AsVoidPtr(py_head), icon, opname); + + Py_RETURN_NONE; +} + +static PyObject *Method_defButO( PyObject * self, PyObject * args ) +{ + PyObject *py_block; + char *opname, *butname, *tip; + int exec, xco, yco, width, height; + + if( !PyArg_ParseTuple( args, "O!sisiiiis:defButO", &PyCObject_Type, &py_block, &opname, &exec, &butname, &xco, &yco, &width, &height, &tip)) + return NULL; + + return PyCObject_FromVoidPtr(uiDefButO(PyCObject_AsVoidPtr(py_block), BUT, opname, exec, butname, xco, yco, width, height, tip), NULL ); +} + +static uiBlock *py_internal_uiBlockCreateFunc(struct bContext *C, struct ARegion *ar, void *arg1) +{ + PyObject *ret, *args; + + args = Py_BuildValue("(NN)", PyCObject_FromVoidPtr(C, NULL), PyCObject_FromVoidPtr(ar, NULL)); + ret = PyObject_CallObject( (PyObject *)arg1, args ); + Py_DECREF(args); + + if (ret==NULL) { + PyErr_Print(); + return NULL; + } + if (!PyCObject_Check(ret)) { + printf("invalid return value, not a PyCObject block\n"); + return NULL; + } + + return (uiBlock *)PyCObject_AsVoidPtr(ret); +} + +static PyObject *Method_pupBlock( PyObject * self, PyObject * args ) +{ + PyObject *py_context, *py_func; + + if( !PyArg_ParseTuple( args, "O!O:pupBlock", &PyCObject_Type, &py_context, &py_func) ) + return NULL; + + if (!PyCallable_Check(py_func)) { + PyErr_SetString(PyExc_ValueError, "arg not callable"); + return NULL; + } + + uiPupBlock(PyCObject_AsVoidPtr(py_context), py_internal_uiBlockCreateFunc, (void *)py_func); + Py_RETURN_NONE; +} + +static PyObject *Method_beginBlock( PyObject * self, PyObject * args ) // XXX missing 2 args - UI_EMBOSS, UI_HELV, do we care? +{ + PyObject *py_context, *py_ar; + char *name; + + if( !PyArg_ParseTuple( args, "O!O!s:beginBlock", &PyCObject_Type, &py_context, &PyCObject_Type, &py_ar, &name) ) + return NULL; + + return PyCObject_FromVoidPtr(uiBeginBlock(PyCObject_AsVoidPtr(py_context), PyCObject_AsVoidPtr(py_ar), name, UI_EMBOSS, UI_HELV), NULL); +} + +static PyObject *Method_endBlock( PyObject * self, PyObject * args ) +{ + PyObject *py_context, *py_block; + + if( !PyArg_ParseTuple( args, "O!O!:endBlock", &PyCObject_Type, &py_context, &PyCObject_Type, &py_block) ) + return NULL; + + uiEndBlock(PyCObject_AsVoidPtr(py_context), PyCObject_AsVoidPtr(py_block)); + Py_RETURN_NONE; +} + +static PyObject *Method_popupBoundsBlock( PyObject * self, PyObject * args ) +{ + PyObject *py_block; + int addval, mx, my; + + if( !PyArg_ParseTuple( args, "O!iii:popupBoundsBlock", &PyCObject_Type, &py_block, &addval, &mx, &my) ) + return NULL; + + uiPopupBoundsBlock(PyCObject_AsVoidPtr(py_block), addval, mx, my); + Py_RETURN_NONE; +} + +static PyObject *Method_blockBeginAlign( PyObject * self, PyObject * args ) +{ + PyObject *py_block; + + if( !PyArg_ParseTuple( args, "O!:blockBeginAlign", &PyCObject_Type, &py_block) ) + return NULL; + + uiBlockBeginAlign(PyCObject_AsVoidPtr(py_block)); + Py_RETURN_NONE; +} + +static PyObject *Method_blockEndAlign( PyObject * self, PyObject * args ) +{ + PyObject *py_block; + + if( !PyArg_ParseTuple( args, "O!:blockEndAlign", &PyCObject_Type, &py_block)) + return NULL; + + uiBlockEndAlign(PyCObject_AsVoidPtr(py_block)); + Py_RETURN_NONE; +} + +static PyObject *Method_newPanel( PyObject * self, PyObject * args ) +{ + PyObject *py_context, *py_area, *py_block; + char *panelname, *tabname; + int ofsx, ofsy, sizex, sizey; + + if( !PyArg_ParseTuple( args, "O!O!O!ssiiii:newPanel", &PyCObject_Type, &py_context, &PyCObject_Type, &py_area, &PyCObject_Type, &py_block, &panelname, &tabname, &ofsx, &ofsy, &sizex, &sizey)) + return NULL; + + return PyLong_FromSize_t(uiNewPanel(PyCObject_AsVoidPtr(py_context), PyCObject_AsVoidPtr(py_area), PyCObject_AsVoidPtr(py_block), panelname, tabname, ofsx, ofsy, sizex, sizey)); +} + +/* internal use only */ +static bContext *get_py_context__internal(void) +{ + PyObject *globals = PyEval_GetGlobals(); + PyObject *val= PyDict_GetItemString(globals, "__bpy_context__"); + return PyCObject_AsVoidPtr(val); +} + +static PyObject *Method_getRegonPtr( PyObject * self ) +{ + bContext *C= get_py_context__internal(); + + ARegion *ar = CTX_wm_region(C); + return PyCObject_FromVoidPtr(ar, NULL); +} + +static PyObject *Method_getAreaPtr( PyObject * self ) +{ + bContext *C= get_py_context__internal(); + + ScrArea *area = CTX_wm_area(C); + return PyCObject_FromVoidPtr(area, NULL); +} + +static PyObject *Method_getScreenPtr( PyObject * self ) +{ + bContext *C= get_py_context__internal(); + + bScreen *screen= CTX_wm_screen(C); + return PyCObject_FromVoidPtr(screen, NULL); +} + +static PyObject *Method_getWindowPtr( PyObject * self ) +{ + bContext *C= get_py_context__internal(); + + wmWindow *window= CTX_wm_window(C); + return PyCObject_FromVoidPtr(window, NULL); +} + +static struct PyMethodDef ui_methods[] = { + {"pupMenuBegin", (PyCFunction)Method_pupMenuBegin, METH_VARARGS, ""}, + {"pupMenuEnd", (PyCFunction)Method_pupMenuEnd, METH_VARARGS, ""}, + {"menuItemO", (PyCFunction)Method_menuItemO, METH_VARARGS, ""}, + {"defButO", (PyCFunction)Method_defButO, METH_VARARGS, ""}, + {"pupBlock", (PyCFunction)Method_pupBlock, METH_VARARGS, ""}, + {"beginBlock", (PyCFunction)Method_beginBlock, METH_VARARGS, ""}, + {"endBlock", (PyCFunction)Method_endBlock, METH_VARARGS, ""}, + {"popupBoundsBlock", (PyCFunction)Method_popupBoundsBlock, METH_VARARGS, ""}, + {"blockBeginAlign", (PyCFunction)Method_blockBeginAlign, METH_VARARGS, ""}, + {"blockEndAlign", (PyCFunction)Method_blockEndAlign, METH_VARARGS, ""}, + {"newPanel", (PyCFunction)Method_newPanel, METH_VARARGS, ""}, + + {"getRegonPtr", (PyCFunction)Method_getRegonPtr, METH_NOARGS, ""}, // XXX Nasty, we really need to improve dealing with context! + {"getAreaPtr", (PyCFunction)Method_getAreaPtr, METH_NOARGS, ""}, + {"getScreenPtr", (PyCFunction)Method_getScreenPtr, METH_NOARGS, ""}, + {"getWindowPtr", (PyCFunction)Method_getWindowPtr, METH_NOARGS, ""}, + {NULL, NULL, 0, NULL} +}; + +PyObject *BPY_ui_module( void ) +{ + PyObject *submodule; + submodule = Py_InitModule3( "bpyui", ui_methods, "" ); + return submodule; +} diff --git a/source/blender/python/intern/bpy_ui.h b/source/blender/python/intern/bpy_ui.h new file mode 100644 index 00000000000..4182a32d3f0 --- /dev/null +++ b/source/blender/python/intern/bpy_ui.h @@ -0,0 +1,31 @@ +/** + * $Id$ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Contributor(s): Campbell Barton + * + * ***** END GPL LICENSE BLOCK ***** + */ +#ifndef BPY_UI_H__ +#define BPY_UI_H__ + +#include + +PyObject *BPY_ui_module( void ); + +#endif