/* ----------------------------------------------------------------------------- This source file is part of OpenSpace3D For the latest info, see http://www.openspace3d.com Copyright (c) 2012 I-maginer This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser 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, or go to http://www.gnu.org/copyleft/lesser.txt ----------------------------------------------------------------------------- */ struct AvPlug = [ PAV_instance : PInstance, PAV_netcomm : NetComm, PAV_sGroupName : S, PAV_sMeshPath : S, PAV_sMatPath : S, PAV_sPhotoPath : S, PAV_lAvatars : [[NetUser Avatar] r1], PAV_lastPos : [[F F F] [F F F F]], PAV_sLastAnimName : S, PAV_font : ObjFont, PAV_iTick : I ]mkAvPlug;; //TODO what to do on room change ? //TODO face material with user picture //TODO plane for user name label struct AvTooltip = [ AVT_widget : SO3_WIDGET, AVT_size : [I I], AVT_state : I ]mkAvTooltip;; struct Avatar = [ AV_user : NetUser, AV_shell : SO3_OBJECT, AV_mesh : SO3_OBJECT, AV_material : SO3_MATERIAL, AV_texture : SO3_TEXTURE, AV_lAnims : [[S V3Danim] r1], AV_sCurrentAnim : S, AV_lastPos : [[F F F] [F F F F]], AV_nextPos : [[F F F] [F F F F]], AV_lerpCoef : F, AV_label : AvTooltip, AV_state : I ]mkAvatar;; // delete an avatar with it's resources fun deleteAvatar(avstr)= let sizelist avstr.AV_lAnims -> size in let 0 -> i in while (i < size) do ( let nth_list avstr.AV_lAnims i -> [_ animstr] in V3DdelAnimation c3dXsession animstr; set i = i + 1; ); //detroy associated tooltip V3DremoveWidgetControl (V3DgetDefaultViewport (V3DgetSessionView c3dXsession)) avstr.AV_label.AVT_widget; SO3WidgetDestroy avstr.AV_label.AVT_widget; SO3ObjectDestroy avstr.AV_shell; SO3ObjectDestroy avstr.AV_mesh; // delete material and texture SO3MaterialDestroy avstr.AV_material; SO3TextureDestroy avstr.AV_texture; 0;; // delete all instance resources fun deleteOb(inst, obstr)= //delete all users resources let sizelist obstr.PAV_lAvatars -> size in let 0 -> i in while (i < size) do ( let nth_list obstr.PAV_lAvatars i -> [userstr avstr] in deleteAvatar avstr; set i = i + 1; ); set obstr.PAV_lAvatars = nil; SO3GroupDelete (V3DgetSession c3dXsession) obstr.PAV_sGroupName; _DSfont obstr.PAV_font; 0;; fun vecLerp(v1, v2, coef)= let multiplyVectorF v2 [coef coef coef] -> v1t in let multiplyVectorF v1 [(1.0 -. coef) (1.0 -. coef) (1.0 -. coef)] -> v2t in addVectorF v1t v2t;; // smooth positions fun avLerp(avstr, speed)= let avstr.AV_lastPos -> [lvec lquat] in let avstr.AV_nextPos -> [nvec nquat] in if (avstr.AV_lerpCoef >=. 1.0) || (vectorEqualF lvec [0.0 0.0 0.0]) then ( set avstr.AV_lerpCoef = 1.0; SO3ObjectSetPosition avstr.AV_shell nvec; SO3ObjectSetOrientation avstr.AV_shell nquat; ) else ( set avstr.AV_lerpCoef = avstr.AV_lerpCoef +. speed; let vecLerp lvec nvec avstr.AV_lerpCoef -> dvec in let SO3MathsQuatInterpolate lquat nquat avstr.AV_lerpCoef 1 -> dquat in ( SO3ObjectSetPosition avstr.AV_shell dvec; SO3ObjectSetOrientation avstr.AV_shell dquat; ); ); 0;; // send the new user position fun sendAvPos(obstr, pos)= let pos -> [[vx vy vz] [ax ay az aw]] in netUpdateUserItem obstr.PAV_netcomm "av_pos" (strbuild ((ftoa vx)::(ftoa vy)::(ftoa vz)::nil)::((ftoa ax)::(ftoa ay)::(ftoa az)::(ftoa aw)::nil)::nil); 0;; fun sendAvAnim(obstr, animname)= if (!strcmp obstr.PAV_sLastAnimName animname) then nil else ( netUpdateUserItem obstr.PAV_netcomm "av_anim" animname; set obstr.PAV_sLastAnimName = animname; ); 0;; fun sendAvPhoto(obstr, bmp)= let G2DstrechBitmap _channel bmp 256 256 0 -> strchbmp in let strcatn "tmp/avatars/"::(itoa netThisId obstr.PAV_netcomm)::".jpg"::nil -> path in let strcatn "tmp/avatars/"::"photo"::".jpg"::nil -> savepath in ( _SAVEjpeg strchbmp _getmodifypack savepath 70; let substr _fileSign _checkpack savepath 1 1024 -> sign in let strcatn "tmp/avatars/"::sign::".jpg"::nil -> path in ( _storepack (_getpack _checkpack savepath) path; _DSbitmap bmp; _DSbitmap strchbmp; netUpdateUserItem obstr.PAV_netcomm "av_photo" path; ); ); 0;; fun updateAvPhoto(obstr, avstr, path)= let (itoa (netUserGetId avstr.AV_user)) -> idx in let G2DloadBmp _channel path -> bmp in ( SO3TextureBlit avstr.AV_texture bmp; _DSbitmap bmp; ); 0;; // update or hide avatar tooltip fun updateAvLabelPos(avstr)= let V3DgetDefaultViewport (V3DgetSessionView c3dXsession) -> viewportstr in let SO3ObjectGetPosition avstr.AV_shell -> vec in let SO3ViewportGetPixelPositionFromWorldPos viewportstr.V3D_viewport vec -> pos in if (pos == nil) then ( SO3WidgetSetVisibility avstr.AV_label.AVT_widget 0; ) else ( let pos -> [x y] in let avstr.AV_label.AVT_size -> [w h] in SO3WidgetSetPosition avstr.AV_label.AVT_widget (x - w / 2) y - 100; SO3WidgetSetVisibility avstr.AV_label.AVT_widget 1; ); 0;; fun vector3EqualF(vec1, vec2, tolerance)= let vec1 -> [x1 y1 z1] in let vec2 -> [x2 y2 z2] in ((absf x1 -. x2) <=. tolerance) && ((absf y1 -. y2) <=. tolerance) && ((absf z1 -. z2) <=. tolerance);; fun vector4EqualF(vec1, vec2, tolerance)= let vec1 -> [x1 y1 z1 w1] in let vec2 -> [x2 y2 z2 w2] in ((absf x1 -. x2) <=. tolerance) && ((absf y1 -. y2) <=. tolerance) && ((absf z1 -. z2) <=. tolerance) && ((absf w1 -. w2) <=. tolerance);; fun cbNavPreRender(inst, viewstr, etime, obstr)= let obstr.PAV_lastPos -> [[lx ly lz] [lqx lqy lqz lqw]] in let V3DgetObjectByName c3dXsession "Current camera" -> cam in let SO3ObjectGetGlobalPosition cam -> [x y z] in let SO3ObjectGetGlobalOrientation cam -> [qx qy qz qw] in ( if ((_tickcount - obstr.PAV_iTick) < 100) then nil else ( if ((vector3EqualF [x y z] [lx ly lz] 0.0001) && (vector4EqualF [qx qy qz qw] [lqx lqy lqz lqw] 0.0001)) then ( sendAvAnim obstr "idle"; ) else ( sendAvPos obstr [[x y z] [qx qy qz qw]]; sendAvAnim obstr "walk"; ); set obstr.PAV_lastPos = [[x y z] [qx qy qz qw]]; set obstr.PAV_iTick = _tickcount; ); ); let sizelist obstr.PAV_lAvatars -> size in let 0 -> i in while (i < size) do ( let nth_list obstr.PAV_lAvatars i -> [userstr avstr] in let (mod 600 etime) -> tm in let if (tm == 0) then 1 else tm -> tm in ( avLerp avstr (itof etime) /. (600.0 *. (itof tm)); updateAvLabelPos avstr; //TODO walk animation with animation fade int / out ); set i = i + 1; ); 0;; fun cbAvAnimEnd(trm, p)= let p -> [canimstr avstr] in if (((SO3AnimationGetTimePosition canimstr.V3D_anim) <. (SO3AnimationGetLength canimstr.V3D_anim)) && !SO3AnimationGetLoop canimstr.V3D_anim) then nil else ( V3DstopAnimation canimstr; V3DplayAnimation (switchstr avstr.AV_lAnims avstr.AV_sCurrentAnim); _deltimer trm; ); 0;; fun cbGetUserItem(inst, netstr, userstr, item, value, obstr)= if (userstr == (netThisUser netstr)) then nil else let switch obstr.PAV_lAvatars userstr -> avstr in if (avstr == nil) then nil else ( if (!strcmp item "av_pos") then ( let strextr value -> l in let hd l -> sv in let hd tl l -> sa in let [atof(nth_list sv 0) atof(nth_list sv 1) atof(nth_list sv 2)] -> vec in let [atof(nth_list sa 0) atof(nth_list sa 1) atof(nth_list sa 2) atof(nth_list sa 3)] -> ang in ( set avstr.AV_lastPos = [SO3ObjectGetPosition avstr.AV_shell SO3ObjectGetOrientation avstr.AV_shell]; set avstr.AV_nextPos = [vec ang]; set avstr.AV_lerpCoef = 0.0; ); 0; ) else if (!strcmp item "av_anim") then ( //wait for the animation to end let (switchstr avstr.AV_lAnims avstr.AV_sCurrentAnim) -> canimstr in if (((SO3AnimationGetTimePosition canimstr.V3D_anim) <. (SO3AnimationGetLength canimstr.V3D_anim)) && !SO3AnimationGetLoop canimstr.V3D_anim) then ( _rfltimer _starttimer _channel 100 @cbAvAnimEnd [canimstr avstr]; 0; ) else ( V3DstopAnimation (switchstr avstr.AV_lAnims avstr.AV_sCurrentAnim); V3DplayAnimation (switchstr avstr.AV_lAnims value); 0; ); set avstr.AV_sCurrentAnim = value; 0; ) else if (!strcmp item "av_photo") && (value != nil) then ( //request the file if (_checkpack value) != nil then ( updateAvPhoto obstr avstr value; ) else ( netGetFile netstr userstr nil "avphoto" value; ); 0; ) else nil; ); 0;; fun createAvLabel(obstr, userstr, login)= let V3DgetDefaultViewport (V3DgetSessionView c3dXsession) -> viewportstr in let if (login == nil) then netUserGetLogin userstr else login -> login in let (itoa (netUserGetId userstr)) -> idx in let [10 10] -> [xmargin ymargin] in let G2DgetStringSize obstr.PAV_font login -> [tw th] in //text height let [(tw + (xmargin * 2)) (th + (ymargin * 2))] -> [texWidth texHeight] in let _FILLbitmap (_CRbitmap _channel texWidth texHeight) 0xffffff -> bmpRgb in let _FILLbitmap8 (_CRbitmap8 _channel texWidth texHeight) 0x000000 -> bmp8 in let _DRAWtext bmpRgb obstr.PAV_font (texWidth / 2) (texHeight / 2) - (th / 2) TD_CENTER 0x000000 login -> bmpRgb in let _DRAWtext8 bmp8 obstr.PAV_font (texWidth / 2) (texHeight / 2) - (th / 2) TD_CENTER 0xffffff login -> bmp8 in let _CRalphaBitmap _channel bmpRgb bmp8 nil nil -> abmp in let SO3BitmapWidgetCreate (V3DgetSession c3dXsession) viewportstr.V3D_viewport (strcatn idx::(getPluginInstanceName obstr.PAV_instance)::"_tooltip"::nil) 0 0 texWidth texHeight 0 -> widget in ( V3DaddWidgetControl viewportstr widget; SO3WidgetSetVisibility widget 0; SO3WidgetSetTransparency widget 1; SO3WidgetSetOpacity widget 0.9; SO3WidgetSetKeyboardEnable widget 0; SO3WidgetSetMouseEnable widget 0; SO3BitmapWidgetBlitAlpha widget abmp; _DSbitmap bmpRgb; _DSbitmap8 bmp8; _DSalphaBitmap abmp; mkAvTooltip [widget [texWidth texHeight] 1]; );; fun updateAvLabel(obstr, avstr, login)= V3DremoveWidgetControl (V3DgetDefaultViewport (V3DgetSessionView c3dXsession)) avstr.AV_label.AVT_widget; SO3WidgetDestroy avstr.AV_label.AVT_widget; set avstr.AV_label = createAvLabel obstr avstr.AV_user login; 0;; fun cbLoginChanged(inst, netstr, userstr, newlogin, obstr)= let switch obstr.PAV_lAvatars userstr -> avstr in if (avstr == nil) then nil else ( updateAvLabel obstr avstr newlogin; ); 0;; fun cbGetFile(inst, netstr, userstr, cmd, data, path, obstr)= let switch obstr.PAV_lAvatars userstr -> avstr in if (avstr == nil) || (strcmp cmd "avphoto") then nil else ( let (itoa (netUserGetId userstr)) -> idx in _storepack data path; updateAvPhoto obstr avstr path; ); 0;; fun cbSetAnimation(inst, from, action, param, rep, obstr)= sendAvAnim obstr param; 0;; fun cbDlgChangePhoto(dlg, obstr, pfile)= let G2DloadBmpPack _channel pfile -> bmp in ( if (bmp == nil) then nil else ( sendAvPhoto obstr bmp; ); ); 0;; fun cbChangePhoto(inst, from, action, param, rep, obstr)= _DLGrflopen _DLGOpenFile _channel c3dXwin nil nil "Bitmaps\0*.bmp;*.jpg;*.png;*.tga;*.dds;\0Bitmap\0*.bmp\0Jpeg\0*.jpg\0Png\0*.png\0Targa\0*.tga\0All\0*.*\0\0" @cbDlgChangePhoto obstr; 0;; fun cbAddUser(inst, netstr, userstr, obstr)= if (userstr == (netThisUser netstr)) then nil else let (itoa (netUserGetId userstr)) -> idx in let V3DaddShell c3dXsession strcatn idx::"_av_shell_"::(getPluginInstanceName inst)::nil nil nil [0.0 0.0 0.0] nil -> shell in let SO3SceneLoadEntity (V3DgetSession c3dXsession) obstr.PAV_sGroupName (strcatn idx::"_av_mesh_"::(getPluginInstanceName inst)::nil) (_checkpack obstr.PAV_sMeshPath) -> avatar in let createAvLabel obstr userstr nil -> avlabelstr in let mkAvatar[userstr shell avatar nil nil nil "idle" [[0.0 0.0 0.0] [0.0 0.0 0.0 1.0]] [[0.0 0.0 0.0] [0.0 0.0 0.0 1.0]] 1.0 avlabelstr 0] -> avstr in ( SO3ObjectLink avatar shell; SO3ObjectSetOrientation avatar [0.0 1.0 0.0 0.0]; SO3ObjectSetScale avatar [0.5 0.5 0.5]; let SO3ObjectGetVertexAnimations avatar -> lanims in let sizelist lanims -> size in let 0 -> i in while (i < size) do ( let nth_list lanims i -> anim in let if i == 0 then 1 else 0 -> defstate in let SO3AnimationGetName anim -> animname in ( let V3DaddMorphAnimation c3dXsession anim avatar (atoi idx) (strcatn animname::"_"::(getPluginInstanceName inst)::nil) (strcatn idx::"."::animname::"_"::(getPluginInstanceName inst)::nil) 1.0 1.0 defstate 0 -> animstr in set avstr.AV_lAnims = [animname animstr]::avstr.AV_lAnims; //addLogMessage animname; ); set i = i + 1; ); V3DsetAnimationLoop (switchstr avstr.AV_lAnims "idle") 1; V3DsetAnimationLoop (switchstr avstr.AV_lAnims "walk") 1; V3DplayAnimation (switchstr avstr.AV_lAnims "idle"); // manage material let SO3SceneGetMaterial (V3DgetSession c3dXsession) obstr.PAV_sGroupName "av_face" -> fmat in let SO3MaterialCreate (V3DgetSession c3dXsession) (strcatn idx::"av_face"::nil) obstr.PAV_sGroupName -> nmat in //_checkpack "logo.bmp" let SO3TextureCreate (V3DgetSession c3dXsession) (strcatn idx::"av_tex"::nil) nil obstr.PAV_sGroupName 256 256 -> ntex in ( SO3MaterialSetAmbientByTechAndPass nmat 0 0 (SO3MaterialGetAmbientByTechAndPass fmat 0 0); SO3MaterialSetDiffuseByTechAndPass nmat 0 0 (SO3MaterialGetDiffuseByTechAndPass fmat 0 0); SO3MaterialSetSelfIlluminationByTechAndPass nmat 0 0 (SO3MaterialGetSelfIlluminationByTechAndPass fmat 0 0); SO3MaterialSetShininessByTechAndPass nmat 0 0 (SO3MaterialGetShininessByTechAndPass fmat 0 0); SO3MaterialSetSpecularByTechAndPass nmat 0 0 (SO3MaterialGetSpecularByTechAndPass fmat 0 0); SO3MaterialSetTexture nmat ntex 0 0 0; SO3EntitySetMaterial avstr.AV_mesh nmat 1; set avstr.AV_material = nmat; set avstr.AV_texture = ntex; ); set obstr.PAV_lAvatars = [userstr avstr]::obstr.PAV_lAvatars; ); 0;; fun cbDelUser(inst, netstr, userstr, obstr)= if (userstr == (netThisUser netstr)) then nil else ( let switch obstr.PAV_lAvatars userstr -> avstr in if (avstr == nil) then nil else ( deleteAvatar avstr; ); set obstr.PAV_lAvatars = remove_idx_from_list obstr.PAV_lAvatars userstr; ); 0;; fun cbDisconnected(inst, netstr, obstr)= //delete all users resources let sizelist obstr.PAV_lAvatars -> size in let 0 -> i in while (i < size) do ( let nth_list obstr.PAV_lAvatars i -> [userstr avstr] in deleteAvatar avstr; set i = i + 1; ); set obstr.PAV_lAvatars = nil; 0;; fun cbSConnected(inst, netstr, obstr)= let V3DgetObjectByName c3dXsession "Current camera" -> cam in let SO3ObjectGetGlobalPosition cam -> vec in let SO3ObjectGetGlobalOrientation cam -> quat in ( sendAvPos obstr [vec quat]; //set current avatar let _checkpack strcatn "tmp/avatars/"::"photo"::".jpg"::nil -> photo in if (photo == nil) then nil else ( let substr _fileSign photo 1 1024 -> sign in let strcatn "tmp/avatars/"::sign::".jpg"::nil -> path in ( _storepack (_getpack photo) path; netUpdateUserItem netstr "av_photo" path; ); ); ); 0;; fun newOb(inst)= let strcat (getPluginInstanceName inst) "_Group" -> grpname in let strcatn (getPluginDirectory (getInstancePlugin inst))::"/res/"::"avatar.mesh"::nil -> meshpath in let strcatn (getPluginDirectory (getInstancePlugin inst))::"/res/"::"avatar.material"::nil -> matpath in let "logo.bmp" -> photopath in let G2DcrFont _channel 12 0 FF_WEIGHT "Lucida Console" -> font in let mkAvPlug [inst netcomOS3D grpname meshpath matpath photopath nil [[0.0 0.0 0.0] [0.0 0.0 0.0 1.0]] "idle" font _tickcount] -> obstr in ( SO3GroupCreate (V3DgetSession c3dXsession) grpname; V3DaddResource c3dXsession matpath grpname SO3_RESOURCE_MATERIAL; V3DaddResource c3dXsession meshpath grpname SO3_RESOURCE_MESH; setPluginInstanceCbNetNewUser inst mkfun4 @cbAddUser obstr; setPluginInstanceCbNetDelUser inst mkfun4 @cbDelUser obstr; setPluginInstanceCbNetUserGetItem inst mkfun6 @cbGetUserItem obstr; setPluginInstanceCbNetSConnected inst mkfun3 @cbSConnected obstr; setPluginInstanceCbNetClosed inst mkfun3 @cbDisconnected obstr; setPluginInstanceCbNetUserChangeLogin inst mkfun5 @cbLoginChanged obstr; setPluginInstanceCbNetGetFile inst mkfun7 @cbGetFile obstr; setPluginInstanceCbScenePreRender inst mkfun4 @cbNavPreRender obstr; PluginRegisterAction inst "Set animation" mkfun6 @cbSetAnimation obstr; PluginRegisterAction inst "Change photo" mkfun6 @cbChangePhoto obstr; setPluginInstanceCbDel inst mkfun2 @deleteOb obstr; ); 0;; fun IniPlug(file)= PlugRegister @newOb nil; setPluginEditor @dynamicedit; 0;;